@iiwish/agentrecord 0.0.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.
@@ -0,0 +1,228 @@
1
+ import fs from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { execFileSync } from "node:child_process";
5
+
6
+ export const CONFIG_FILE = "agentrecord.config.json";
7
+ export const SUPPORTED_LOCALES = new Set(["en-US", "zh-CN", "auto"]);
8
+ export const SUPPORTED_LABEL_MODES = new Set(["localized", "canonical", "bilingual-compact"]);
9
+ export const DEFAULT_AUDIENCES = ["self", "share"];
10
+
11
+ export function safePathSegment(value) {
12
+ return String(value || "default").replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "default";
13
+ }
14
+
15
+ export function expandHome(value) {
16
+ if (!value) return value;
17
+ if (value === "~") return process.env.HOME || value;
18
+ if (value.startsWith("~/")) return path.join(process.env.HOME || "", value.slice(2));
19
+ return value;
20
+ }
21
+
22
+ export function resolvePath(value, baseDir = process.cwd()) {
23
+ const expanded = expandHome(value);
24
+ if (!expanded) return expanded;
25
+ return path.isAbsolute(expanded) ? expanded : path.resolve(baseDir, expanded);
26
+ }
27
+
28
+ export function normalizeLocale(value, fallback = "auto") {
29
+ const raw = String(value || "").trim();
30
+ if (!raw) return fallback;
31
+ const normalized = raw.replace("_", "-").toLowerCase();
32
+ if (["auto", "detect"].includes(normalized)) return "auto";
33
+ if (normalized === "zh" || normalized.startsWith("zh-") || normalized === "cn") return "zh-CN";
34
+ if (normalized === "en" || normalized.startsWith("en-")) return "en-US";
35
+ return SUPPORTED_LOCALES.has(raw) ? raw : fallback;
36
+ }
37
+
38
+ export function normalizeLabelMode(value) {
39
+ const raw = String(value || "").trim();
40
+ return SUPPORTED_LABEL_MODES.has(raw) ? raw : "bilingual-compact";
41
+ }
42
+
43
+ export function normalizeAudiences(value) {
44
+ const allowed = new Set(["self", "share", "hiring", "job-agent"]);
45
+ const raw = Array.isArray(value) ? value : String(value || "").split(",");
46
+ const audiences = unique(raw.map((item) => String(item).trim()).filter((item) => allowed.has(item)));
47
+ const defaultOnly = audiences.length ? audiences : DEFAULT_AUDIENCES;
48
+ return defaultOnly.includes("self") ? defaultOnly : ["self", ...defaultOnly];
49
+ }
50
+
51
+ export function splitPathList(value) {
52
+ return String(value || "").split(new RegExp(`[${escapeRegExp(path.delimiter)},]`)).map((item) => item.trim()).filter(Boolean);
53
+ }
54
+
55
+ export function unique(values) {
56
+ return [...new Set(values.filter(Boolean))];
57
+ }
58
+
59
+ export function readJsonIfExists(file) {
60
+ try {
61
+ return JSON.parse(fs.readFileSync(file, "utf8"));
62
+ } catch {
63
+ return null;
64
+ }
65
+ }
66
+
67
+ export function writeJson(file, value) {
68
+ fs.writeFileSync(file, `${JSON.stringify(value, null, 2)}\n`);
69
+ }
70
+
71
+ export function gitUserName(cwd = process.cwd()) {
72
+ try {
73
+ const value = execFileSync("git", ["config", "--get", "user.name"], {
74
+ cwd,
75
+ encoding: "utf8",
76
+ stdio: ["ignore", "pipe", "ignore"]
77
+ }).trim();
78
+ return value || null;
79
+ } catch {
80
+ return null;
81
+ }
82
+ }
83
+
84
+ export function defaultOwnerDisplayName() {
85
+ return process.env.AGENTRECORD_OWNER || gitUserName() || os.userInfo().username || "default";
86
+ }
87
+
88
+ export function defaultOwner() {
89
+ return safePathSegment(defaultOwnerDisplayName());
90
+ }
91
+
92
+ export function createDefaultConfig({ owner, profilesDir = "profiles", locale = "auto" } = {}) {
93
+ const ownerDisplayName = owner || defaultOwnerDisplayName();
94
+ const safeOwner = safePathSegment(ownerDisplayName);
95
+ return {
96
+ schema_version: "agentrecord.config.v0",
97
+ owner: safeOwner,
98
+ owner_display_name: ownerDisplayName,
99
+ profiles_dir: profilesDir,
100
+ output: {
101
+ profile_dir: `${profilesDir}/${safeOwner}`
102
+ },
103
+ codex: {
104
+ sessions_dir: "~/.codex/sessions",
105
+ session_roots: ["~/.codex/sessions"],
106
+ account_usage: {
107
+ enabled: true,
108
+ timeout_ms: 15000
109
+ }
110
+ },
111
+ memory: {
112
+ enabled: true,
113
+ registry_path: "~/.codex/memories/MEMORY.md"
114
+ },
115
+ evidence_rules_paths: [
116
+ "references/evidence-rules.json"
117
+ ],
118
+ report: {
119
+ locale,
120
+ fallback_locale: "en-US",
121
+ label_mode: "bilingual-compact",
122
+ schema_language: "en-US",
123
+ audiences: DEFAULT_AUDIENCES,
124
+ default_audience: "self"
125
+ },
126
+ privacy: {
127
+ public_session_ids: false,
128
+ public_project_paths: false
129
+ }
130
+ };
131
+ }
132
+
133
+ export function loadConfig(options = {}) {
134
+ const configPath = resolvePath(options.config || process.env.AGENTRECORD_CONFIG || CONFIG_FILE);
135
+ const configDir = path.dirname(configPath);
136
+ const rawConfig = readJsonIfExists(configPath) || {};
137
+ const envOwner = process.env.AGENTRECORD_OWNER;
138
+ const hasOwnerOverride = Boolean(options.owner || envOwner);
139
+ const hasProfilesDirOverride = Boolean(options.profilesDir || process.env.AGENTRECORD_PROFILES_DIR);
140
+ const ownerInput = options.owner || envOwner || rawConfig.owner || rawConfig.profile_owner || defaultOwnerDisplayName();
141
+ const owner = safePathSegment(rawConfig.owner_id || ownerInput);
142
+ const ownerDisplayName = rawConfig.owner_display_name || rawConfig.identity?.display_name || ownerInput;
143
+ const profilesDirRaw = options.profilesDir || process.env.AGENTRECORD_PROFILES_DIR || rawConfig.profiles_dir || rawConfig.output?.profiles_dir || "profiles";
144
+ const profileDirRaw = options.output
145
+ || (hasOwnerOverride || hasProfilesDirOverride
146
+ ? path.join(profilesDirRaw, owner)
147
+ : rawConfig.output?.profile_dir || path.join(profilesDirRaw, owner));
148
+ const profileDir = resolvePath(profileDirRaw, configDir);
149
+ const profilesDir = resolvePath(profilesDirRaw, configDir);
150
+ const privateStateDir = path.join(profileDir, ".agentrecord");
151
+ const sessionsOverride = options.codexSessionsDir || options.sessionsDir || process.env.AGENTRECORD_CODEX_SESSIONS_DIR;
152
+ const configuredSessionRoots = rawConfig.codex?.session_roots || rawConfig.codex?.sessions_roots || rawConfig.codex?.sessions_dirs;
153
+ const configuredSessionDir = rawConfig.codex?.sessions_dir || rawConfig.codex?.session_dir;
154
+ const sessionRootsRaw = sessionsOverride
155
+ ? splitPathList(sessionsOverride)
156
+ : configuredSessionRoots
157
+ ? Array.isArray(configuredSessionRoots) ? configuredSessionRoots : splitPathList(configuredSessionRoots)
158
+ : [configuredSessionDir || "~/.codex/sessions"];
159
+ const accountUsageConfig = rawConfig.codex?.account_usage || {};
160
+ const accountUsageEnabled = typeof options.accountUsage === "boolean"
161
+ ? options.accountUsage
162
+ : accountUsageConfig.enabled !== false;
163
+ const accountUsageTimeoutMs = Number(options.accountUsageTimeoutMs || process.env.AGENTRECORD_CODEX_ACCOUNT_USAGE_TIMEOUT_MS || accountUsageConfig.timeout_ms || 15000);
164
+ const privacyMode = options.privacy || rawConfig.privacy?.mode || "strict";
165
+ const publicProjectPaths = typeof options.publicProjectPaths === "boolean"
166
+ ? options.publicProjectPaths
167
+ : privacyMode === "open"
168
+ ? true
169
+ : rawConfig.privacy?.public_project_paths === true;
170
+ const publicSessionIds = typeof options.publicSessionIds === "boolean"
171
+ ? options.publicSessionIds
172
+ : rawConfig.privacy?.public_session_ids === true;
173
+ const locale = normalizeLocale(options.locale || process.env.AGENTRECORD_LOCALE || rawConfig.report?.locale || rawConfig.locale || "auto");
174
+ const fallbackLocale = normalizeLocale(rawConfig.report?.fallback_locale || process.env.AGENTRECORD_FALLBACK_LOCALE || "en-US", "en-US");
175
+ const evidenceRulesRaw = options.evidenceRules
176
+ ? splitPathList(options.evidenceRules)
177
+ : Array.isArray(rawConfig.evidence_rules_paths)
178
+ ? rawConfig.evidence_rules_paths
179
+ : rawConfig.evidence_rules_paths
180
+ ? splitPathList(rawConfig.evidence_rules_paths)
181
+ : ["references/evidence-rules.json"];
182
+
183
+ return {
184
+ configPath,
185
+ configDir,
186
+ exists: fs.existsSync(configPath),
187
+ raw: rawConfig,
188
+ resolved: {
189
+ owner,
190
+ ownerDisplayName,
191
+ profilesDir,
192
+ profileDir,
193
+ privateStateDir,
194
+ codex: {
195
+ sessionRoots: unique(sessionRootsRaw.map((item) => resolvePath(item, configDir))),
196
+ accountUsage: {
197
+ enabled: accountUsageEnabled,
198
+ timeoutMs: Number.isFinite(accountUsageTimeoutMs) ? accountUsageTimeoutMs : 15000,
199
+ executable: process.env.AGENTRECORD_CODEX_BIN || accountUsageConfig.executable || "codex"
200
+ }
201
+ },
202
+ memory: {
203
+ enabled: rawConfig.memory?.enabled !== false,
204
+ registryPath: rawConfig.memory?.enabled === false
205
+ ? null
206
+ : resolvePath(options.memoryFile || process.env.AGENTRECORD_MEMORY_FILE || rawConfig.memory?.registry_path || "~/.codex/memories/MEMORY.md", configDir)
207
+ },
208
+ evidenceRulesPaths: unique(evidenceRulesRaw.map((item) => resolvePath(item, configDir))),
209
+ report: {
210
+ locale,
211
+ fallbackLocale: fallbackLocale === "auto" ? "en-US" : fallbackLocale,
212
+ labelMode: normalizeLabelMode(options.labelMode || rawConfig.report?.label_mode || "bilingual-compact"),
213
+ schemaLanguage: "en-US",
214
+ audiences: normalizeAudiences(options.audiences || rawConfig.report?.audiences || DEFAULT_AUDIENCES),
215
+ defaultAudience: rawConfig.report?.default_audience || "self"
216
+ },
217
+ privacy: {
218
+ mode: privacyMode,
219
+ publicSessionIds,
220
+ publicProjectPaths
221
+ }
222
+ }
223
+ };
224
+ }
225
+
226
+ function escapeRegExp(value) {
227
+ return String(value).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
228
+ }