vigthoria-cli 1.10.37 → 1.10.47
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/commands/agent-session-menu.d.ts +19 -0
- package/dist/commands/agent-session-menu.js +155 -0
- package/dist/commands/auth.js +68 -51
- package/dist/commands/bridge.js +19 -12
- package/dist/commands/cancel.js +22 -15
- package/dist/commands/chat.d.ts +0 -28
- package/dist/commands/chat.js +407 -1254
- package/dist/commands/config.js +73 -33
- package/dist/commands/deploy.js +123 -83
- package/dist/commands/device.js +61 -21
- package/dist/commands/edit.js +39 -32
- package/dist/commands/explain.js +25 -18
- package/dist/commands/generate.js +44 -37
- package/dist/commands/hub.js +102 -95
- package/dist/commands/index.js +46 -41
- package/dist/commands/legion.js +186 -146
- package/dist/commands/review.js +36 -29
- package/dist/commands/security.js +12 -5
- package/dist/commands/wallet.js +35 -28
- package/dist/commands/workflow.js +20 -13
- package/dist/utils/brain-hub-client.js +5 -1
- package/dist/utils/bridge-client.js +52 -11
- package/dist/utils/codebase-indexer.js +41 -4
- package/dist/utils/context-ranker.js +21 -15
- package/dist/utils/files.js +42 -5
- package/dist/utils/logger.js +50 -42
- package/dist/utils/persona.js +8 -3
- package/dist/utils/post-write-validator.js +29 -22
- package/dist/utils/project-memory.js +23 -16
- package/dist/utils/task-display.js +20 -13
- package/dist/utils/workspace-brain-service.js +45 -8
- package/dist/utils/workspace-cache.js +26 -18
- package/dist/utils/workspace-stream.js +63 -21
- package/package.json +3 -6
- package/dist/commands/fork.d.ts +0 -17
- package/dist/commands/fork.js +0 -164
- package/dist/commands/history.d.ts +0 -17
- package/dist/commands/history.js +0 -113
- package/dist/commands/preview.d.ts +0 -55
- package/dist/commands/preview.js +0 -467
- package/dist/commands/replay.d.ts +0 -18
- package/dist/commands/replay.js +0 -156
- package/dist/commands/repo.d.ts +0 -97
- package/dist/commands/repo.js +0 -773
- package/dist/commands/update.d.ts +0 -9
- package/dist/commands/update.js +0 -201
- package/dist/index.d.ts +0 -21
- package/dist/index.js +0 -1823
- package/dist/utils/api.d.ts +0 -572
- package/dist/utils/api.js +0 -6548
- package/dist/utils/cli-state.d.ts +0 -54
- package/dist/utils/cli-state.js +0 -185
- package/dist/utils/config.d.ts +0 -85
- package/dist/utils/config.js +0 -267
- package/dist/utils/session.d.ts +0 -118
- package/dist/utils/session.js +0 -423
- package/dist/utils/tools.d.ts +0 -276
- package/dist/utils/tools.js +0 -3516
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Vigthoria CLI — shared local-state helpers.
|
|
3
|
-
*
|
|
4
|
-
* Single source for:
|
|
5
|
-
* • Offline-mode detection (VIGTHORIA_OFFLINE=1) — suppresses every
|
|
6
|
-
* background network call (update checks, gateway preflights, etc.).
|
|
7
|
-
* • Telemetry opt-out (VIGTHORIA_TELEMETRY_DISABLED=1).
|
|
8
|
-
* • Update-check cache, so `npm view` is not invoked on every CLI start.
|
|
9
|
-
* • Cross-platform safe file mode enforcement for tokens / sessions.
|
|
10
|
-
*
|
|
11
|
-
* No external network calls — pure local helpers. Safe to import from
|
|
12
|
-
* any command module.
|
|
13
|
-
*/
|
|
14
|
-
/** 24h is plenty for an update-check ping. */
|
|
15
|
-
export declare const UPDATE_CHECK_TTL_MS: number;
|
|
16
|
-
/** 60s gateway-preflight cache so back-to-back CLI invocations stay fast. */
|
|
17
|
-
export declare const GATEWAY_PREFLIGHT_TTL_MS: number;
|
|
18
|
-
export interface CliStateShape {
|
|
19
|
-
updateCheck?: {
|
|
20
|
-
checkedAt: number;
|
|
21
|
-
latestVersion: string;
|
|
22
|
-
notifiedFor?: string;
|
|
23
|
-
};
|
|
24
|
-
gatewayPreflight?: {
|
|
25
|
-
checkedAt: number;
|
|
26
|
-
valid: boolean;
|
|
27
|
-
};
|
|
28
|
-
/** Last-seen version — used to render release notes on upgrade. */
|
|
29
|
-
lastSeenVersion?: string;
|
|
30
|
-
}
|
|
31
|
-
export declare function ensureStateDir(): void;
|
|
32
|
-
/** Best-effort lock-down: `0o600` on POSIX, no-op on Windows (NTFS ACL ≠ chmod). */
|
|
33
|
-
export declare function secureFileMode(filePath: string): void;
|
|
34
|
-
/** Atomic write helper — write to temp then rename, never half-written JSON. */
|
|
35
|
-
export declare function atomicWriteJson(targetPath: string, value: unknown, mode?: number): void;
|
|
36
|
-
export declare function loadCliState(): CliStateShape;
|
|
37
|
-
export declare function saveCliState(state: CliStateShape): void;
|
|
38
|
-
export declare function getCliStateFile(): string;
|
|
39
|
-
/**
|
|
40
|
-
* Offline mode — set VIGTHORIA_OFFLINE=1 to skip every non-essential
|
|
41
|
-
* outbound HTTP call. Useful for restricted networks, air-gapped runs,
|
|
42
|
-
* and deterministic CI.
|
|
43
|
-
*/
|
|
44
|
-
export declare function isOfflineMode(): boolean;
|
|
45
|
-
/** Telemetry opt-out (also implied when offline mode is on). */
|
|
46
|
-
export declare function isTelemetryDisabled(): boolean;
|
|
47
|
-
/** When true the startup update check is suppressed (env override or offline). */
|
|
48
|
-
export declare function isUpdateCheckSuppressed(): boolean;
|
|
49
|
-
/** Cached read for the latest known version — null when stale or absent. */
|
|
50
|
-
export declare function readCachedLatestVersion(maxAgeMs?: number): string | null;
|
|
51
|
-
export declare function writeCachedLatestVersion(latestVersion: string, notifiedFor?: string): void;
|
|
52
|
-
export declare function readGatewayPreflightCache(maxAgeMs?: number): boolean | null;
|
|
53
|
-
export declare function writeGatewayPreflightCache(valid: boolean): void;
|
|
54
|
-
export declare function isSafeNpmPackageSpec(spec: string): boolean;
|
package/dist/utils/cli-state.js
DELETED
|
@@ -1,185 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Vigthoria CLI — shared local-state helpers.
|
|
3
|
-
*
|
|
4
|
-
* Single source for:
|
|
5
|
-
* • Offline-mode detection (VIGTHORIA_OFFLINE=1) — suppresses every
|
|
6
|
-
* background network call (update checks, gateway preflights, etc.).
|
|
7
|
-
* • Telemetry opt-out (VIGTHORIA_TELEMETRY_DISABLED=1).
|
|
8
|
-
* • Update-check cache, so `npm view` is not invoked on every CLI start.
|
|
9
|
-
* • Cross-platform safe file mode enforcement for tokens / sessions.
|
|
10
|
-
*
|
|
11
|
-
* No external network calls — pure local helpers. Safe to import from
|
|
12
|
-
* any command module.
|
|
13
|
-
*/
|
|
14
|
-
import * as fs from 'fs';
|
|
15
|
-
import * as os from 'os';
|
|
16
|
-
import * as path from 'path';
|
|
17
|
-
const STATE_DIR = path.join(os.homedir(), '.vigthoria');
|
|
18
|
-
const STATE_FILE = path.join(STATE_DIR, 'cli-state.json');
|
|
19
|
-
/** 24h is plenty for an update-check ping. */
|
|
20
|
-
export const UPDATE_CHECK_TTL_MS = 24 * 60 * 60 * 1000;
|
|
21
|
-
/** 60s gateway-preflight cache so back-to-back CLI invocations stay fast. */
|
|
22
|
-
export const GATEWAY_PREFLIGHT_TTL_MS = 60 * 1000;
|
|
23
|
-
function readStateUnchecked() {
|
|
24
|
-
try {
|
|
25
|
-
if (!fs.existsSync(STATE_FILE)) {
|
|
26
|
-
return {};
|
|
27
|
-
}
|
|
28
|
-
const text = fs.readFileSync(STATE_FILE, 'utf8');
|
|
29
|
-
if (!text.trim()) {
|
|
30
|
-
return {};
|
|
31
|
-
}
|
|
32
|
-
const parsed = JSON.parse(text);
|
|
33
|
-
return parsed && typeof parsed === 'object' ? parsed : {};
|
|
34
|
-
}
|
|
35
|
-
catch {
|
|
36
|
-
// Corrupt state file should never crash the CLI — treat as empty.
|
|
37
|
-
return {};
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
export function ensureStateDir() {
|
|
41
|
-
try {
|
|
42
|
-
if (!fs.existsSync(STATE_DIR)) {
|
|
43
|
-
fs.mkdirSync(STATE_DIR, { recursive: true, mode: 0o700 });
|
|
44
|
-
}
|
|
45
|
-
if (process.platform !== 'win32') {
|
|
46
|
-
try {
|
|
47
|
-
fs.chmodSync(STATE_DIR, 0o700);
|
|
48
|
-
}
|
|
49
|
-
catch { /* best-effort */ }
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
catch {
|
|
53
|
-
// Best-effort — caller will see write failure later if it matters.
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
/** Best-effort lock-down: `0o600` on POSIX, no-op on Windows (NTFS ACL ≠ chmod). */
|
|
57
|
-
export function secureFileMode(filePath) {
|
|
58
|
-
if (process.platform === 'win32') {
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
try {
|
|
62
|
-
fs.chmodSync(filePath, 0o600);
|
|
63
|
-
}
|
|
64
|
-
catch {
|
|
65
|
-
// File may not exist yet, or filesystem may not support chmod.
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
/** Atomic write helper — write to temp then rename, never half-written JSON. */
|
|
69
|
-
export function atomicWriteJson(targetPath, value, mode = 0o600) {
|
|
70
|
-
ensureStateDir();
|
|
71
|
-
const parent = path.dirname(targetPath);
|
|
72
|
-
try {
|
|
73
|
-
if (!fs.existsSync(parent))
|
|
74
|
-
fs.mkdirSync(parent, { recursive: true });
|
|
75
|
-
}
|
|
76
|
-
catch { /* ignore */ }
|
|
77
|
-
const tmpPath = `${targetPath}.${process.pid}.${Date.now()}.tmp`;
|
|
78
|
-
fs.writeFileSync(tmpPath, JSON.stringify(value, null, 2), 'utf8');
|
|
79
|
-
if (process.platform !== 'win32') {
|
|
80
|
-
try {
|
|
81
|
-
fs.chmodSync(tmpPath, mode);
|
|
82
|
-
}
|
|
83
|
-
catch { /* ignore */ }
|
|
84
|
-
}
|
|
85
|
-
fs.renameSync(tmpPath, targetPath);
|
|
86
|
-
}
|
|
87
|
-
export function loadCliState() {
|
|
88
|
-
return readStateUnchecked();
|
|
89
|
-
}
|
|
90
|
-
export function saveCliState(state) {
|
|
91
|
-
atomicWriteJson(STATE_FILE, state);
|
|
92
|
-
}
|
|
93
|
-
export function getCliStateFile() {
|
|
94
|
-
return STATE_FILE;
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* Offline mode — set VIGTHORIA_OFFLINE=1 to skip every non-essential
|
|
98
|
-
* outbound HTTP call. Useful for restricted networks, air-gapped runs,
|
|
99
|
-
* and deterministic CI.
|
|
100
|
-
*/
|
|
101
|
-
export function isOfflineMode() {
|
|
102
|
-
const flag = String(process.env.VIGTHORIA_OFFLINE || '').trim().toLowerCase();
|
|
103
|
-
return flag === '1' || flag === 'true' || flag === 'yes';
|
|
104
|
-
}
|
|
105
|
-
/** Telemetry opt-out (also implied when offline mode is on). */
|
|
106
|
-
export function isTelemetryDisabled() {
|
|
107
|
-
if (isOfflineMode())
|
|
108
|
-
return true;
|
|
109
|
-
const flag = String(process.env.VIGTHORIA_TELEMETRY_DISABLED || '').trim().toLowerCase();
|
|
110
|
-
return flag === '1' || flag === 'true' || flag === 'yes';
|
|
111
|
-
}
|
|
112
|
-
/** When true the startup update check is suppressed (env override or offline). */
|
|
113
|
-
export function isUpdateCheckSuppressed() {
|
|
114
|
-
if (isOfflineMode())
|
|
115
|
-
return true;
|
|
116
|
-
const flag = String(process.env.VIGTHORIA_NO_UPDATE_CHECK || '').trim();
|
|
117
|
-
return flag === '1';
|
|
118
|
-
}
|
|
119
|
-
/** Cached read for the latest known version — null when stale or absent. */
|
|
120
|
-
export function readCachedLatestVersion(maxAgeMs = UPDATE_CHECK_TTL_MS) {
|
|
121
|
-
const state = readStateUnchecked();
|
|
122
|
-
const entry = state.updateCheck;
|
|
123
|
-
if (!entry || !entry.latestVersion || typeof entry.checkedAt !== 'number') {
|
|
124
|
-
return null;
|
|
125
|
-
}
|
|
126
|
-
if (Date.now() - entry.checkedAt > maxAgeMs) {
|
|
127
|
-
return null;
|
|
128
|
-
}
|
|
129
|
-
return entry.latestVersion;
|
|
130
|
-
}
|
|
131
|
-
export function writeCachedLatestVersion(latestVersion, notifiedFor) {
|
|
132
|
-
try {
|
|
133
|
-
const state = readStateUnchecked();
|
|
134
|
-
state.updateCheck = {
|
|
135
|
-
checkedAt: Date.now(),
|
|
136
|
-
latestVersion,
|
|
137
|
-
notifiedFor: notifiedFor || state.updateCheck?.notifiedFor,
|
|
138
|
-
};
|
|
139
|
-
saveCliState(state);
|
|
140
|
-
}
|
|
141
|
-
catch {
|
|
142
|
-
// Caching is a nice-to-have, never block CLI on write failure.
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
export function readGatewayPreflightCache(maxAgeMs = GATEWAY_PREFLIGHT_TTL_MS) {
|
|
146
|
-
const state = readStateUnchecked();
|
|
147
|
-
const entry = state.gatewayPreflight;
|
|
148
|
-
if (!entry || typeof entry.checkedAt !== 'number' || typeof entry.valid !== 'boolean') {
|
|
149
|
-
return null;
|
|
150
|
-
}
|
|
151
|
-
if (Date.now() - entry.checkedAt > maxAgeMs) {
|
|
152
|
-
return null;
|
|
153
|
-
}
|
|
154
|
-
return entry.valid;
|
|
155
|
-
}
|
|
156
|
-
export function writeGatewayPreflightCache(valid) {
|
|
157
|
-
try {
|
|
158
|
-
const state = readStateUnchecked();
|
|
159
|
-
state.gatewayPreflight = { checkedAt: Date.now(), valid };
|
|
160
|
-
saveCliState(state);
|
|
161
|
-
}
|
|
162
|
-
catch {
|
|
163
|
-
// Best-effort.
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
/**
|
|
167
|
-
* Validate an npm package spec used by `vigthoria update --from <spec>`.
|
|
168
|
-
* Returns `true` when the spec is acceptable. Rejects whitespace and
|
|
169
|
-
* shell metacharacters so the spec can never break out of `execFile` /
|
|
170
|
-
* a Windows `shell:true` fallback into command injection.
|
|
171
|
-
*/
|
|
172
|
-
const NPM_SPEC_ALLOWLIST = /^[A-Za-z0-9_./@:+\\=-]+$/;
|
|
173
|
-
const DISALLOWED_SHELL_CHARS = /[;&|`$<>(){}\[\]"'\s]/;
|
|
174
|
-
export function isSafeNpmPackageSpec(spec) {
|
|
175
|
-
const candidate = String(spec || '').trim();
|
|
176
|
-
if (!candidate)
|
|
177
|
-
return false;
|
|
178
|
-
if (candidate.length > 512)
|
|
179
|
-
return false;
|
|
180
|
-
if (DISALLOWED_SHELL_CHARS.test(candidate))
|
|
181
|
-
return false;
|
|
182
|
-
if (!NPM_SPEC_ALLOWLIST.test(candidate))
|
|
183
|
-
return false;
|
|
184
|
-
return true;
|
|
185
|
-
}
|
package/dist/utils/config.d.ts
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Configuration Manager for Vigthoria CLI
|
|
3
|
-
*/
|
|
4
|
-
export interface VigthoriaCLIConfig {
|
|
5
|
-
apiUrl: string;
|
|
6
|
-
modelsApiUrl: string;
|
|
7
|
-
wsUrl: string;
|
|
8
|
-
selfHostedModelsApiUrl: string | null;
|
|
9
|
-
authToken: string | null;
|
|
10
|
-
refreshToken: string | null;
|
|
11
|
-
userId: string | null;
|
|
12
|
-
email: string | null;
|
|
13
|
-
v3ServiceKey: string | null;
|
|
14
|
-
subscription: {
|
|
15
|
-
plan: string | null;
|
|
16
|
-
status: string | null;
|
|
17
|
-
expiresAt: string | null;
|
|
18
|
-
};
|
|
19
|
-
preferences: {
|
|
20
|
-
defaultModel: string;
|
|
21
|
-
theme: 'dark' | 'light';
|
|
22
|
-
autoApplyFixes: boolean;
|
|
23
|
-
showDiffs: boolean;
|
|
24
|
-
contextLines: number;
|
|
25
|
-
maxTokens: number;
|
|
26
|
-
};
|
|
27
|
-
project: {
|
|
28
|
-
rootPath: string | null;
|
|
29
|
-
ignorePatterns: string[];
|
|
30
|
-
};
|
|
31
|
-
persona: 'default' | 'wiener_grant';
|
|
32
|
-
hubModelPrefs?: {
|
|
33
|
-
enabledCloudModels: string[];
|
|
34
|
-
defaultCloudModel?: string;
|
|
35
|
-
defaultLocalModel?: string;
|
|
36
|
-
balance?: number;
|
|
37
|
-
fetchedAt?: string;
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
export interface AvailableModelDescriptor {
|
|
41
|
-
id: string;
|
|
42
|
-
name: string;
|
|
43
|
-
description: string;
|
|
44
|
-
tier: string;
|
|
45
|
-
backendModel: string;
|
|
46
|
-
}
|
|
47
|
-
export declare class Config {
|
|
48
|
-
private store;
|
|
49
|
-
private static readonly OPERATOR_PLANS;
|
|
50
|
-
private static readonly CLOUD_PLANS;
|
|
51
|
-
constructor();
|
|
52
|
-
/**
|
|
53
|
-
* Re-apply secure POSIX permissions to the on-disk config file. Safe
|
|
54
|
-
* to call repeatedly; no-op on Windows.
|
|
55
|
-
*/
|
|
56
|
-
secureOnDisk(): void;
|
|
57
|
-
get<K extends keyof VigthoriaCLIConfig>(key: K): VigthoriaCLIConfig[K];
|
|
58
|
-
set<K extends keyof VigthoriaCLIConfig>(key: K, value: VigthoriaCLIConfig[K]): void;
|
|
59
|
-
getAll(): VigthoriaCLIConfig;
|
|
60
|
-
reset(): void;
|
|
61
|
-
isAuthenticated(): boolean;
|
|
62
|
-
hasValidSubscription(): boolean;
|
|
63
|
-
getNormalizedPlan(): string;
|
|
64
|
-
hasOperatorAccess(): boolean;
|
|
65
|
-
hasCloudAccess(): boolean;
|
|
66
|
-
getConfigPath(): string;
|
|
67
|
-
setAuth(data: {
|
|
68
|
-
token: string;
|
|
69
|
-
refreshToken?: string;
|
|
70
|
-
userId: string;
|
|
71
|
-
email: string;
|
|
72
|
-
}): void;
|
|
73
|
-
clearAuth(): void;
|
|
74
|
-
setSubscription(data: {
|
|
75
|
-
plan: string;
|
|
76
|
-
status: string;
|
|
77
|
-
expiresAt?: string;
|
|
78
|
-
}): void;
|
|
79
|
-
getAvailableModels(): AvailableModelDescriptor[];
|
|
80
|
-
refreshHubModelPreferences(): Promise<void>;
|
|
81
|
-
isCloudModel(modelId: string): boolean;
|
|
82
|
-
filterModelsByHubPreferences(models: AvailableModelDescriptor[], enabledCloudModels?: string[]): AvailableModelDescriptor[];
|
|
83
|
-
isComplexTask(prompt: string): boolean;
|
|
84
|
-
shouldUseCloudForHeavyTask(prompt: string): boolean;
|
|
85
|
-
}
|
package/dist/utils/config.js
DELETED
|
@@ -1,267 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Configuration Manager for Vigthoria CLI
|
|
3
|
-
*/
|
|
4
|
-
import Conf from 'conf';
|
|
5
|
-
import { secureFileMode } from './cli-state.js';
|
|
6
|
-
const defaultConfig = {
|
|
7
|
-
apiUrl: 'https://coder.vigthoria.io',
|
|
8
|
-
modelsApiUrl: 'https://api.vigthoria.io', // Direct AI Models API - no proxying through Coder
|
|
9
|
-
wsUrl: 'wss://coder.vigthoria.io/ws',
|
|
10
|
-
selfHostedModelsApiUrl: null,
|
|
11
|
-
authToken: null,
|
|
12
|
-
refreshToken: null,
|
|
13
|
-
userId: null,
|
|
14
|
-
email: null,
|
|
15
|
-
v3ServiceKey: null,
|
|
16
|
-
subscription: {
|
|
17
|
-
plan: null,
|
|
18
|
-
status: null,
|
|
19
|
-
expiresAt: null,
|
|
20
|
-
},
|
|
21
|
-
preferences: {
|
|
22
|
-
defaultModel: 'code',
|
|
23
|
-
theme: 'dark',
|
|
24
|
-
autoApplyFixes: false,
|
|
25
|
-
showDiffs: true,
|
|
26
|
-
contextLines: 3,
|
|
27
|
-
maxTokens: 4096,
|
|
28
|
-
},
|
|
29
|
-
persona: 'default',
|
|
30
|
-
project: {
|
|
31
|
-
rootPath: null,
|
|
32
|
-
ignorePatterns: [
|
|
33
|
-
'node_modules',
|
|
34
|
-
'.git',
|
|
35
|
-
'dist',
|
|
36
|
-
'build',
|
|
37
|
-
'__pycache__',
|
|
38
|
-
'.venv',
|
|
39
|
-
'venv',
|
|
40
|
-
'.env',
|
|
41
|
-
'*.log',
|
|
42
|
-
],
|
|
43
|
-
},
|
|
44
|
-
};
|
|
45
|
-
export class Config {
|
|
46
|
-
store;
|
|
47
|
-
static OPERATOR_PLANS = new Set(['enterprise', 'admin', 'master_admin']);
|
|
48
|
-
static CLOUD_PLANS = new Set(['pro', 'professional', 'ultra', 'enterprise', 'master_admin', 'admin']);
|
|
49
|
-
constructor() {
|
|
50
|
-
// `Conf` defaults to 0o644 on POSIX which would leave auth tokens
|
|
51
|
-
// readable by every local user. We post-process the file in
|
|
52
|
-
// `secureOnDisk()` to enforce 0o600 ownership. The `fileMode`
|
|
53
|
-
// option exists on newer `conf` releases — supply it via a cast so
|
|
54
|
-
// older type-defs do not reject the field.
|
|
55
|
-
this.store = new Conf({
|
|
56
|
-
projectName: 'vigthoria-cli',
|
|
57
|
-
defaults: defaultConfig,
|
|
58
|
-
...{ fileMode: 0o600 },
|
|
59
|
-
schema: {
|
|
60
|
-
apiUrl: { type: 'string' },
|
|
61
|
-
modelsApiUrl: { type: 'string' },
|
|
62
|
-
wsUrl: { type: 'string' },
|
|
63
|
-
selfHostedModelsApiUrl: { type: ['string', 'null'] },
|
|
64
|
-
authToken: { type: ['string', 'null'] },
|
|
65
|
-
refreshToken: { type: ['string', 'null'] },
|
|
66
|
-
userId: { type: ['string', 'null'] },
|
|
67
|
-
email: { type: ['string', 'null'] },
|
|
68
|
-
v3ServiceKey: { type: ['string', 'null'] },
|
|
69
|
-
subscription: {
|
|
70
|
-
type: 'object',
|
|
71
|
-
properties: {
|
|
72
|
-
plan: { type: ['string', 'null'] },
|
|
73
|
-
status: { type: ['string', 'null'] },
|
|
74
|
-
expiresAt: { type: ['string', 'null'] },
|
|
75
|
-
},
|
|
76
|
-
},
|
|
77
|
-
preferences: {
|
|
78
|
-
type: 'object',
|
|
79
|
-
properties: {
|
|
80
|
-
defaultModel: { type: 'string' },
|
|
81
|
-
theme: { type: 'string', enum: ['dark', 'light'] },
|
|
82
|
-
autoApplyFixes: { type: 'boolean' },
|
|
83
|
-
showDiffs: { type: 'boolean' },
|
|
84
|
-
contextLines: { type: 'number' },
|
|
85
|
-
maxTokens: { type: 'number' },
|
|
86
|
-
},
|
|
87
|
-
},
|
|
88
|
-
persona: { type: 'string', enum: ['default', 'wiener_grant'] },
|
|
89
|
-
project: {
|
|
90
|
-
type: 'object',
|
|
91
|
-
properties: {
|
|
92
|
-
rootPath: { type: ['string', 'null'] },
|
|
93
|
-
ignorePatterns: { type: 'array', items: { type: 'string' } },
|
|
94
|
-
},
|
|
95
|
-
},
|
|
96
|
-
},
|
|
97
|
-
});
|
|
98
|
-
// Defence-in-depth: re-apply 0600 every time we instantiate Config so
|
|
99
|
-
// that an older install which created the file with 0644 gets locked
|
|
100
|
-
// down on next startup.
|
|
101
|
-
try {
|
|
102
|
-
secureFileMode(this.store.path);
|
|
103
|
-
}
|
|
104
|
-
catch { /* best-effort */ }
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* Re-apply secure POSIX permissions to the on-disk config file. Safe
|
|
108
|
-
* to call repeatedly; no-op on Windows.
|
|
109
|
-
*/
|
|
110
|
-
secureOnDisk() {
|
|
111
|
-
try {
|
|
112
|
-
secureFileMode(this.store.path);
|
|
113
|
-
}
|
|
114
|
-
catch { /* best-effort */ }
|
|
115
|
-
}
|
|
116
|
-
get(key) {
|
|
117
|
-
return this.store.get(key);
|
|
118
|
-
}
|
|
119
|
-
set(key, value) {
|
|
120
|
-
this.store.set(key, value);
|
|
121
|
-
}
|
|
122
|
-
getAll() {
|
|
123
|
-
return this.store.store;
|
|
124
|
-
}
|
|
125
|
-
reset() {
|
|
126
|
-
this.store.clear();
|
|
127
|
-
}
|
|
128
|
-
isAuthenticated() {
|
|
129
|
-
return this.store.get('authToken') !== null;
|
|
130
|
-
}
|
|
131
|
-
hasValidSubscription() {
|
|
132
|
-
const sub = this.store.get('subscription');
|
|
133
|
-
if (!sub.status || sub.status !== 'active')
|
|
134
|
-
return false;
|
|
135
|
-
if (sub.expiresAt) {
|
|
136
|
-
const expires = new Date(sub.expiresAt);
|
|
137
|
-
return expires > new Date();
|
|
138
|
-
}
|
|
139
|
-
return true;
|
|
140
|
-
}
|
|
141
|
-
getNormalizedPlan() {
|
|
142
|
-
return (this.store.get('subscription').plan || '').trim().toLowerCase();
|
|
143
|
-
}
|
|
144
|
-
hasOperatorAccess() {
|
|
145
|
-
return Config.OPERATOR_PLANS.has(this.getNormalizedPlan());
|
|
146
|
-
}
|
|
147
|
-
hasCloudAccess() {
|
|
148
|
-
return Config.CLOUD_PLANS.has(this.getNormalizedPlan());
|
|
149
|
-
}
|
|
150
|
-
getConfigPath() {
|
|
151
|
-
return this.store.path;
|
|
152
|
-
}
|
|
153
|
-
// Auth helpers
|
|
154
|
-
setAuth(data) {
|
|
155
|
-
this.store.set('authToken', data.token);
|
|
156
|
-
if (data.refreshToken) {
|
|
157
|
-
this.store.set('refreshToken', data.refreshToken);
|
|
158
|
-
}
|
|
159
|
-
this.store.set('userId', data.userId);
|
|
160
|
-
this.store.set('email', data.email);
|
|
161
|
-
// Tighten permissions whenever the token rotates so a brand-new
|
|
162
|
-
// install (which may have created the file under a previous default
|
|
163
|
-
// umask) becomes 0600 immediately after first login.
|
|
164
|
-
this.secureOnDisk();
|
|
165
|
-
}
|
|
166
|
-
clearAuth() {
|
|
167
|
-
this.store.set('authToken', null);
|
|
168
|
-
this.store.set('refreshToken', null);
|
|
169
|
-
this.store.set('userId', null);
|
|
170
|
-
this.store.set('email', null);
|
|
171
|
-
this.store.set('subscription', {
|
|
172
|
-
plan: null,
|
|
173
|
-
status: null,
|
|
174
|
-
expiresAt: null,
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
setSubscription(data) {
|
|
178
|
-
this.store.set('subscription', {
|
|
179
|
-
plan: data.plan,
|
|
180
|
-
status: data.status,
|
|
181
|
-
expiresAt: data.expiresAt || null,
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
// Model selection - Vigthoria Models (internal routing IDs only)
|
|
185
|
-
getAvailableModels() {
|
|
186
|
-
const sub = this.store.get('subscription');
|
|
187
|
-
const plan = (sub.plan || '').toLowerCase();
|
|
188
|
-
// ═══════════════════════════════════════════════════════════════
|
|
189
|
-
// Vigthoria server infrastructure operational models
|
|
190
|
-
// ═══════════════════════════════════════════════════════════════
|
|
191
|
-
const models = [
|
|
192
|
-
{ id: 'code', name: 'Vigthoria v3 Code 35B', description: 'Native 35B coding model on Blackwell (V3 pipeline)', tier: 'local', backendModel: 'vigthoria-v3-code-35b' },
|
|
193
|
-
{ id: 'code-35b', name: 'Vigthoria v3 Code 35B', description: 'Same flagship model as code', tier: 'local', backendModel: 'vigthoria-v3-code-35b' },
|
|
194
|
-
{ id: 'code-9b', name: 'Vigthoria v3 Code 9B', description: 'Efficient coding specialist for quick tasks', tier: 'local', backendModel: 'vigthoria-v3-code-9b' },
|
|
195
|
-
{ id: 'balanced', name: 'Vigthoria Balanced 4B', description: 'Balanced general-purpose local model', tier: 'local', backendModel: 'vigthoria-v3-balanced-4b' },
|
|
196
|
-
{ id: 'balanced-4b', name: 'Vigthoria Balanced 4B', description: 'Efficient 4B general-purpose model (Qwen3.5-4B based)', tier: 'local', backendModel: 'vigthoria-v3-balanced-4b' },
|
|
197
|
-
];
|
|
198
|
-
if (this.hasCloudAccess()) {
|
|
199
|
-
models.push({ id: 'cloud-fast', name: 'Vigthoria Cloud Fast', description: 'Fast cloud responses for lighter work', tier: 'cloud', backendModel: 'vigthoria-cloud-fast' }, { id: 'cloud-balanced', name: 'Vigthoria Cloud Balanced', description: 'Default quality/cost balance for chat and coding', tier: 'cloud', backendModel: 'vigthoria-cloud-balanced' }, { id: 'cloud-code', name: 'Vigthoria Cloud Code', description: 'Economical cloud coding and completion', tier: 'cloud', backendModel: 'vigthoria-cloud-code' }, { id: 'cloud-power', name: 'Vigthoria Cloud Power', description: 'Premium general intelligence for demanding work', tier: 'cloud', backendModel: 'vigthoria-cloud-power' }, { id: 'cloud-maximum', name: 'Vigthoria Cloud Maximum', description: 'Maximum power for complex architecture and reviews', tier: 'cloud', backendModel: 'vigthoria-cloud-maximum' },
|
|
200
|
-
// Legacy aliases
|
|
201
|
-
{ id: 'cloud', name: 'Vigthoria Cloud Balanced', description: 'Legacy alias for cloud-balanced', tier: 'cloud', backendModel: 'vigthoria-cloud-balanced' }, { id: 'cloud-reason', name: 'Vigthoria Cloud Balanced', description: 'Legacy alias for cloud-balanced', tier: 'cloud', backendModel: 'vigthoria-cloud-balanced' }, { id: 'ultra', name: 'Vigthoria Cloud Maximum', description: 'Legacy alias for cloud-maximum', tier: 'cloud', backendModel: 'vigthoria-cloud-maximum' });
|
|
202
|
-
}
|
|
203
|
-
const hubPrefs = this.store.get('hubModelPrefs');
|
|
204
|
-
if (hubPrefs?.enabledCloudModels?.length) {
|
|
205
|
-
return this.filterModelsByHubPreferences(models, hubPrefs.enabledCloudModels);
|
|
206
|
-
}
|
|
207
|
-
return models;
|
|
208
|
-
}
|
|
209
|
-
async refreshHubModelPreferences() {
|
|
210
|
-
const token = this.store.get('authToken');
|
|
211
|
-
const apiUrl = String(this.store.get('apiUrl') || '').replace(/\/$/, '');
|
|
212
|
-
if (!token || !apiUrl)
|
|
213
|
-
return;
|
|
214
|
-
try {
|
|
215
|
-
const response = await fetch(`${apiUrl}/api/user/credits/model-preferences`, {
|
|
216
|
-
headers: { Authorization: `Bearer ${token}` },
|
|
217
|
-
signal: AbortSignal.timeout(8000),
|
|
218
|
-
});
|
|
219
|
-
if (!response.ok)
|
|
220
|
-
return;
|
|
221
|
-
const prefs = await response.json();
|
|
222
|
-
if (prefs?.success === false)
|
|
223
|
-
return;
|
|
224
|
-
this.store.set('hubModelPrefs', {
|
|
225
|
-
enabledCloudModels: Array.isArray(prefs.enabledCloudModels) ? prefs.enabledCloudModels : [],
|
|
226
|
-
defaultCloudModel: typeof prefs.defaultCloudModel === 'string' ? prefs.defaultCloudModel : undefined,
|
|
227
|
-
defaultLocalModel: typeof prefs.defaultLocalModel === 'string' ? prefs.defaultLocalModel : undefined,
|
|
228
|
-
balance: typeof prefs.balance === 'number' ? prefs.balance : undefined,
|
|
229
|
-
fetchedAt: new Date().toISOString(),
|
|
230
|
-
});
|
|
231
|
-
}
|
|
232
|
-
catch {
|
|
233
|
-
// Non-fatal: CLI can still run with default model catalog.
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
// Check if a model is a "Cloud" tier model
|
|
237
|
-
isCloudModel(modelId) {
|
|
238
|
-
const cloudModels = ['cloud-fast', 'cloud-balanced', 'cloud-code', 'cloud-power', 'cloud-maximum', 'cloud', 'cloud-reason', 'ultra'];
|
|
239
|
-
return cloudModels.includes(modelId) || modelId.includes('cloud');
|
|
240
|
-
}
|
|
241
|
-
filterModelsByHubPreferences(models, enabledCloudModels) {
|
|
242
|
-
if (!enabledCloudModels || !enabledCloudModels.length)
|
|
243
|
-
return models;
|
|
244
|
-
const enabled = new Set(enabledCloudModels);
|
|
245
|
-
return models.filter((model) => model.tier !== 'cloud' || enabled.has(model.backendModel));
|
|
246
|
-
}
|
|
247
|
-
// Check if task is complex enough to suggest Cloud upgrade
|
|
248
|
-
isComplexTask(prompt) {
|
|
249
|
-
const complexIndicators = [
|
|
250
|
-
/refactor/i, /architect/i, /redesign/i, /migrate/i,
|
|
251
|
-
/multi.?file/i, /multiple files/i, /entire.*project/i,
|
|
252
|
-
/all files/i, /whole.*codebase/i, /implement.*feature/i,
|
|
253
|
-
/create.*system/i, /build.*from.*scratch/i,
|
|
254
|
-
/analyze.*project/i, /review.*codebase/i,
|
|
255
|
-
/audit.*project/i, /audit.*workspace/i,
|
|
256
|
-
/full overview/i, /analyse.*workspace/i,
|
|
257
|
-
/analy[sz]e.*entire/i, /deep audit/i,
|
|
258
|
-
];
|
|
259
|
-
return complexIndicators.some(pattern => pattern.test(prompt));
|
|
260
|
-
}
|
|
261
|
-
shouldUseCloudForHeavyTask(prompt) {
|
|
262
|
-
// DISABLED: Cloud routing is now EXPLICIT ONLY (--model cloud or /cloud command)
|
|
263
|
-
// Do NOT auto-route based on keywords. User must opt-in.
|
|
264
|
-
// If user wants cloud: they'll use --model cloud or request it explicitly.
|
|
265
|
-
return false;
|
|
266
|
-
}
|
|
267
|
-
}
|