@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.
- package/LICENSE +21 -0
- package/README.md +109 -0
- package/examples/basic/README.md +23 -0
- package/examples/basic/agentrecord.config.example.json +36 -0
- package/locales/en-US.json +45 -0
- package/locales/zh-CN.json +45 -0
- package/package.json +36 -0
- package/references/evidence-rules.json +112 -0
- package/references/evidence-rules.zh-CN.json +112 -0
- package/schemas/evidence.schema.json +30 -0
- package/schemas/profile.schema.json +34 -0
- package/scripts/smoke-install.mjs +76 -0
- package/src/build/artifacts.mjs +29 -0
- package/src/build/catalog.mjs +33 -0
- package/src/build/codex-account.mjs +183 -0
- package/src/build/evidence.mjs +143 -0
- package/src/build/paths.mjs +6 -0
- package/src/build/profile.mjs +608 -0
- package/src/build/renderers.mjs +1267 -0
- package/src/build/report.mjs +64 -0
- package/src/build/run-context.mjs +97 -0
- package/src/build/stats.mjs +176 -0
- package/src/build/utils.mjs +50 -0
- package/src/cli.mjs +78 -0
- package/src/commands/build.mjs +75 -0
- package/src/commands/doctor.mjs +20 -0
- package/src/commands/init.mjs +62 -0
- package/src/commands/open.mjs +81 -0
- package/src/commands/scan.mjs +58 -0
- package/src/commands/validate.mjs +365 -0
- package/src/core/args.mjs +60 -0
- package/src/core/config.mjs +228 -0
|
@@ -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
|
+
}
|