-
Notifications
You must be signed in to change notification settings - Fork 51
Expand file tree
/
Copy pathstate.mjs
More file actions
142 lines (121 loc) · 5.07 KB
/
Copy pathstate.mjs
File metadata and controls
142 lines (121 loc) · 5.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
import os from 'node:os';
import path from 'node:path';
import fs from 'node:fs/promises';
import crypto from 'node:crypto';
async function atomicWriteFile(filePath, data, { mode } = {}) {
const dir = path.dirname(filePath);
const tmp = path.join(dir, `.${path.basename(filePath)}.${crypto.randomBytes(8).toString('hex')}.tmp`);
await fs.writeFile(tmp, data, mode ? { encoding: 'utf8', mode } : { encoding: 'utf8' });
await fs.rename(tmp, filePath);
}
export function defaultStateDir() {
return process.env.AGENTIFY_DESKTOP_STATE_DIR || path.join(os.homedir(), '.agentify-desktop');
}
export function tokenPath(stateDir = defaultStateDir()) {
return path.join(stateDir, 'token.txt');
}
export function statePath(stateDir = defaultStateDir()) {
return path.join(stateDir, 'state.json');
}
export function settingsPath(stateDir = defaultStateDir()) {
return path.join(stateDir, 'settings.json');
}
export function defaultSettings() {
return {
browserBackend: 'chrome-cdp',
chromeDebugPort: 9222,
chromeExecutablePath: null,
chromeProfileMode: 'isolated',
chromeProfileName: 'Default',
// Governor defaults (intentionally conservative).
maxInflightQueries: 2,
maxQueriesPerMinute: 12,
minTabGapMs: 1200,
minGlobalGapMs: 200,
// UX defaults.
showTabsByDefault: false,
allowAuthPopups: true,
// Acknowledgment for changing settings (UX only; not required for operation).
acknowledgedAt: null
};
}
export function normalizeSettings(input) {
const d = defaultSettings();
const s = input && typeof input === 'object' ? input : {};
const clampInt = (v, { min, max, fallback }) => {
const n = Number(v);
if (!Number.isFinite(n)) return fallback;
const i = Math.floor(n);
return Math.max(min, Math.min(max, i));
};
const clampMs = (v, { min, max, fallback }) => clampInt(v, { min, max, fallback });
const out = {
browserBackend: ['electron', 'chrome-cdp'].includes(String(s.browserBackend || '').trim().toLowerCase())
? String(s.browserBackend || '').trim().toLowerCase()
: d.browserBackend,
chromeDebugPort: clampInt(s.chromeDebugPort, { min: 1024, max: 65535, fallback: d.chromeDebugPort }),
chromeExecutablePath:
typeof s.chromeExecutablePath === 'string' && s.chromeExecutablePath.trim() ? s.chromeExecutablePath.trim() : null,
chromeProfileMode: ['isolated', 'existing'].includes(String(s.chromeProfileMode || '').trim().toLowerCase())
? String(s.chromeProfileMode || '').trim().toLowerCase()
: d.chromeProfileMode,
chromeProfileName:
typeof s.chromeProfileName === 'string' && s.chromeProfileName.trim() ? s.chromeProfileName.trim() : d.chromeProfileName,
maxInflightQueries: clampInt(s.maxInflightQueries, { min: 1, max: 12, fallback: d.maxInflightQueries }),
maxQueriesPerMinute: clampInt(s.maxQueriesPerMinute, { min: 1, max: 600, fallback: d.maxQueriesPerMinute }),
minTabGapMs: clampMs(s.minTabGapMs, { min: 0, max: 60_000, fallback: d.minTabGapMs }),
minGlobalGapMs: clampMs(s.minGlobalGapMs, { min: 0, max: 10_000, fallback: d.minGlobalGapMs }),
showTabsByDefault: !!s.showTabsByDefault,
allowAuthPopups: typeof s.allowAuthPopups === 'boolean' ? s.allowAuthPopups : d.allowAuthPopups,
acknowledgedAt: typeof s.acknowledgedAt === 'string' && s.acknowledgedAt.trim() ? s.acknowledgedAt.trim() : null
};
return out;
}
export async function ensureStateDir(stateDir = defaultStateDir()) {
await fs.mkdir(stateDir, { recursive: true });
}
export async function readToken(stateDir = defaultStateDir()) {
const tokenFromEnv = (process.env.AGENTIFY_DESKTOP_TOKEN || '').trim();
if (tokenFromEnv) return tokenFromEnv;
try {
return (await fs.readFile(tokenPath(stateDir), 'utf8')).trim();
} catch {
return null;
}
}
export async function writeToken(token, stateDir = defaultStateDir()) {
await ensureStateDir(stateDir);
await atomicWriteFile(tokenPath(stateDir), `${token}\n`, { mode: 0o600 });
}
export async function ensureToken(stateDir = defaultStateDir()) {
const existing = await readToken(stateDir);
if (existing) return existing;
const token = crypto.randomBytes(24).toString('hex');
await writeToken(token, stateDir);
return token;
}
export async function readState(stateDir = defaultStateDir()) {
try {
return JSON.parse(await fs.readFile(statePath(stateDir), 'utf8'));
} catch {
return null;
}
}
export async function writeState(state, stateDir = defaultStateDir()) {
await ensureStateDir(stateDir);
await atomicWriteFile(statePath(stateDir), `${JSON.stringify(state, null, 2)}\n`);
}
export async function readSettings(stateDir = defaultStateDir()) {
try {
const raw = await fs.readFile(settingsPath(stateDir), 'utf8');
return normalizeSettings(JSON.parse(raw || '{}'));
} catch {
return defaultSettings();
}
}
export async function writeSettings(settings, stateDir = defaultStateDir()) {
await ensureStateDir(stateDir);
const normalized = normalizeSettings(settings);
await atomicWriteFile(settingsPath(stateDir), `${JSON.stringify(normalized, null, 2)}\n`, { mode: 0o600 });
return normalized;
}