workspacecord 1.0.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/LICENSE +21 -0
- package/README.md +190 -0
- package/README.zh-CN.md +190 -0
- package/dist/bot-B5HN4ZW6.js +6710 -0
- package/dist/chunk-2LBNM64L.js +84 -0
- package/dist/chunk-K3NQKI34.js +10 -0
- package/dist/chunk-NIXZJTOZ.js +175 -0
- package/dist/chunk-OKI4UVGY.js +221 -0
- package/dist/chunk-TSBM3BNT.js +1224 -0
- package/dist/chunk-WE4X3JB3.js +130 -0
- package/dist/cli.js +71 -0
- package/dist/codex-launcher-IF2IPLBP.js +132 -0
- package/dist/codex-provider-7CI5W34X.js +304 -0
- package/dist/config-cli-F2B5SYHJ.js +120 -0
- package/dist/daemon-NW4WRMQK.js +252 -0
- package/dist/project-cli-FEMPZIRQ.js +121 -0
- package/dist/project-registry-DQT5ORUU.js +32 -0
- package/dist/setup-TKOVXSME.js +262 -0
- package/dist/thread-manager-5T46QTZF.js +78 -0
- package/dist/utils-72GMT2X5.js +36 -0
- package/package.json +79 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
getConfigValue
|
|
4
|
+
} from "./chunk-OKI4UVGY.js";
|
|
5
|
+
|
|
6
|
+
// src/config.ts
|
|
7
|
+
import { homedir } from "os";
|
|
8
|
+
import { join } from "path";
|
|
9
|
+
function required(key) {
|
|
10
|
+
const value = getConfigValue(key);
|
|
11
|
+
if (!value) {
|
|
12
|
+
if (process.env.NODE_ENV === "test" || process.env.VITEST === "true") {
|
|
13
|
+
return `test-${key.toLowerCase()}`;
|
|
14
|
+
}
|
|
15
|
+
console.error(`ERROR: ${key} is not configured.`);
|
|
16
|
+
console.error("Run \x1B[36mworkspacecord config setup\x1B[0m to configure.");
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
return value;
|
|
20
|
+
}
|
|
21
|
+
function optional(key, fallback) {
|
|
22
|
+
return getConfigValue(key) ?? fallback;
|
|
23
|
+
}
|
|
24
|
+
function optionalList(key, fallback = []) {
|
|
25
|
+
const value = getConfigValue(key);
|
|
26
|
+
if (!value) return fallback;
|
|
27
|
+
return value.split(",").map((item) => item.trim()).filter(Boolean);
|
|
28
|
+
}
|
|
29
|
+
function optionalInt(key, fallback) {
|
|
30
|
+
const value = getConfigValue(key);
|
|
31
|
+
if (!value) return fallback;
|
|
32
|
+
const n = parseInt(value, 10);
|
|
33
|
+
return Number.isNaN(n) ? fallback : n;
|
|
34
|
+
}
|
|
35
|
+
function optionalBool(key, fallback) {
|
|
36
|
+
const value = getConfigValue(key);
|
|
37
|
+
if (!value) return fallback;
|
|
38
|
+
return value === "true" || value === "1";
|
|
39
|
+
}
|
|
40
|
+
var config = {
|
|
41
|
+
token: required("DISCORD_TOKEN"),
|
|
42
|
+
clientId: required("DISCORD_CLIENT_ID"),
|
|
43
|
+
guildId: optional("DISCORD_GUILD_ID", ""),
|
|
44
|
+
allowedUsers: optionalList("ALLOWED_USERS"),
|
|
45
|
+
allowAllUsers: optionalBool("ALLOW_ALL_USERS", false),
|
|
46
|
+
dataDir: join(homedir(), ".workspacecord"),
|
|
47
|
+
defaultProvider: optional("DEFAULT_PROVIDER", "codex"),
|
|
48
|
+
defaultMode: optional("DEFAULT_MODE", "auto"),
|
|
49
|
+
claudePermissionMode: optional("CLAUDE_PERMISSION_MODE", "normal"),
|
|
50
|
+
maxSubagentDepth: optionalInt("MAX_SUBAGENT_DEPTH", 3),
|
|
51
|
+
maxActiveSessionsPerProject: optionalInt("MAX_ACTIVE_SESSIONS", 20),
|
|
52
|
+
autoArchiveDays: optionalInt("AUTO_ARCHIVE_DAYS", 7),
|
|
53
|
+
messageRetentionDays: optionalInt("MESSAGE_RETENTION_DAYS", 0),
|
|
54
|
+
rateLimitMs: optionalInt("RATE_LIMIT_MS", 1e3),
|
|
55
|
+
shellEnabled: optionalBool("SHELL_ENABLED", false),
|
|
56
|
+
shellAllowedUsers: optionalList("SHELL_ALLOWED_USERS"),
|
|
57
|
+
codexSandboxMode: optional("CODEX_SANDBOX_MODE", "workspace-write"),
|
|
58
|
+
codexApprovalPolicy: optional("CODEX_APPROVAL_POLICY", "on-failure"),
|
|
59
|
+
codexNetworkAccessEnabled: optionalBool("CODEX_NETWORK_ACCESS_ENABLED", false),
|
|
60
|
+
codexWebSearchMode: optional("CODEX_WEB_SEARCH", "disabled"),
|
|
61
|
+
codexReasoningEffort: optional("CODEX_REASONING_EFFORT", ""),
|
|
62
|
+
codexBaseUrl: optional("CODEX_BASE_URL", ""),
|
|
63
|
+
codexApiKey: optional("CODEX_API_KEY", ""),
|
|
64
|
+
codexPath: optional("CODEX_PATH", ""),
|
|
65
|
+
anthropicApiKey: optional("ANTHROPIC_API_KEY", ""),
|
|
66
|
+
anthropicBaseUrl: optional("ANTHROPIC_BASE_URL", ""),
|
|
67
|
+
sessionSyncIntervalMs: optionalInt("SESSION_SYNC_INTERVAL_MS", 3e4),
|
|
68
|
+
healthReportIntervalMs: optionalInt("HEALTH_REPORT_INTERVAL_MS", 6e5),
|
|
69
|
+
healthReportEnabled: optionalBool("HEALTH_REPORT_ENABLED", true),
|
|
70
|
+
healthCheckStuckThresholdMs: optionalInt("HEALTH_CHECK_STUCK_THRESHOLD_MS", 18e5),
|
|
71
|
+
healthCheckIdleThresholdMs: optionalInt("HEALTH_CHECK_IDLE_THRESHOLD_MS", 72e5)
|
|
72
|
+
};
|
|
73
|
+
if (config.anthropicApiKey) process.env.ANTHROPIC_API_KEY = config.anthropicApiKey;
|
|
74
|
+
if (config.anthropicBaseUrl) process.env.ANTHROPIC_BASE_URL = config.anthropicBaseUrl;
|
|
75
|
+
if (config.allowedUsers.length === 0 && !config.allowAllUsers) {
|
|
76
|
+
if (process.env.NODE_ENV !== "test" && process.env.VITEST !== "true") {
|
|
77
|
+
console.error("ERROR: Set ALLOWED_USERS or ALLOW_ALL_USERS=true");
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export {
|
|
83
|
+
config
|
|
84
|
+
};
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/project-registry.ts
|
|
4
|
+
import { randomUUID } from "crypto";
|
|
5
|
+
import { resolve } from "path";
|
|
6
|
+
|
|
7
|
+
// src/persistence.ts
|
|
8
|
+
import { readFile, writeFile, mkdir, rename } from "fs/promises";
|
|
9
|
+
import { existsSync } from "fs";
|
|
10
|
+
import { dirname, join } from "path";
|
|
11
|
+
import { homedir } from "os";
|
|
12
|
+
var dataDirOverride = null;
|
|
13
|
+
function getDataDir() {
|
|
14
|
+
return dataDirOverride ?? join(homedir(), ".workspacecord");
|
|
15
|
+
}
|
|
16
|
+
var Store = class {
|
|
17
|
+
filename;
|
|
18
|
+
writeQueue = Promise.resolve();
|
|
19
|
+
constructor(filename) {
|
|
20
|
+
this.filename = filename;
|
|
21
|
+
}
|
|
22
|
+
get filePath() {
|
|
23
|
+
return join(getDataDir(), this.filename);
|
|
24
|
+
}
|
|
25
|
+
async read() {
|
|
26
|
+
try {
|
|
27
|
+
const data = await readFile(this.filePath, "utf-8");
|
|
28
|
+
return JSON.parse(data);
|
|
29
|
+
} catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async write(data) {
|
|
34
|
+
const nextWrite = this.writeQueue.catch(() => {
|
|
35
|
+
}).then(async () => {
|
|
36
|
+
const filePath = this.filePath;
|
|
37
|
+
const dir = dirname(filePath);
|
|
38
|
+
if (!existsSync(dir)) {
|
|
39
|
+
await mkdir(dir, { recursive: true });
|
|
40
|
+
}
|
|
41
|
+
const tmpPath = filePath + ".tmp";
|
|
42
|
+
await writeFile(tmpPath, JSON.stringify(data, null, 2), "utf-8");
|
|
43
|
+
await rename(tmpPath, filePath);
|
|
44
|
+
});
|
|
45
|
+
this.writeQueue = nextWrite;
|
|
46
|
+
await nextWrite;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// src/project-registry.ts
|
|
51
|
+
var store = new Store("projects.json");
|
|
52
|
+
var projects = [];
|
|
53
|
+
function normalizePath(path) {
|
|
54
|
+
return resolve(path);
|
|
55
|
+
}
|
|
56
|
+
async function saveRegistry() {
|
|
57
|
+
await store.write(projects);
|
|
58
|
+
}
|
|
59
|
+
async function loadRegistry() {
|
|
60
|
+
projects = await store.read() || [];
|
|
61
|
+
}
|
|
62
|
+
function getProjectByName(name) {
|
|
63
|
+
return projects.find((project) => project.name === name);
|
|
64
|
+
}
|
|
65
|
+
function getProjectByPath(path) {
|
|
66
|
+
const normalized = normalizePath(path);
|
|
67
|
+
return projects.find((project) => normalizePath(project.path) === normalized);
|
|
68
|
+
}
|
|
69
|
+
function getProjectByCategoryId(categoryId) {
|
|
70
|
+
return projects.find((project) => project.discordCategoryId === categoryId);
|
|
71
|
+
}
|
|
72
|
+
function getAllRegisteredProjects() {
|
|
73
|
+
return [...projects];
|
|
74
|
+
}
|
|
75
|
+
async function registerProject(name, path) {
|
|
76
|
+
const normalizedPath = normalizePath(path);
|
|
77
|
+
const existingByPath = getProjectByPath(normalizedPath);
|
|
78
|
+
if (existingByPath) {
|
|
79
|
+
if (existingByPath.name !== name) {
|
|
80
|
+
throw new Error(`Path already registered as project "${existingByPath.name}"`);
|
|
81
|
+
}
|
|
82
|
+
return existingByPath;
|
|
83
|
+
}
|
|
84
|
+
if (getProjectByName(name)) {
|
|
85
|
+
throw new Error(`Project name already exists: ${name}`);
|
|
86
|
+
}
|
|
87
|
+
const now = Date.now();
|
|
88
|
+
const project = {
|
|
89
|
+
id: randomUUID(),
|
|
90
|
+
name,
|
|
91
|
+
path: normalizedPath,
|
|
92
|
+
skills: {},
|
|
93
|
+
mcpServers: [],
|
|
94
|
+
createdAt: now,
|
|
95
|
+
updatedAt: now
|
|
96
|
+
};
|
|
97
|
+
projects.push(project);
|
|
98
|
+
await saveRegistry();
|
|
99
|
+
return project;
|
|
100
|
+
}
|
|
101
|
+
async function renameProject(oldName, newName) {
|
|
102
|
+
const project = getProjectByName(oldName);
|
|
103
|
+
if (!project) throw new Error(`Project not found: ${oldName}`);
|
|
104
|
+
if (oldName !== newName && getProjectByName(newName)) {
|
|
105
|
+
throw new Error(`Project name already exists: ${newName}`);
|
|
106
|
+
}
|
|
107
|
+
project.name = newName;
|
|
108
|
+
project.updatedAt = Date.now();
|
|
109
|
+
await saveRegistry();
|
|
110
|
+
}
|
|
111
|
+
async function removeProject(name) {
|
|
112
|
+
const before = projects.length;
|
|
113
|
+
projects = projects.filter((project) => project.name !== name);
|
|
114
|
+
if (projects.length === before) throw new Error(`Project not found: ${name}`);
|
|
115
|
+
await saveRegistry();
|
|
116
|
+
}
|
|
117
|
+
async function bindProjectCategory(name, categoryId, categoryName) {
|
|
118
|
+
const project = getProjectByName(name);
|
|
119
|
+
if (!project) throw new Error(`Project not found: ${name}`);
|
|
120
|
+
const existing = getProjectByCategoryId(categoryId);
|
|
121
|
+
if (existing && existing.name !== name) {
|
|
122
|
+
throw new Error(`Discord category is already bound to project "${existing.name}"`);
|
|
123
|
+
}
|
|
124
|
+
project.discordCategoryId = categoryId;
|
|
125
|
+
project.discordCategoryName = categoryName;
|
|
126
|
+
project.updatedAt = Date.now();
|
|
127
|
+
await saveRegistry();
|
|
128
|
+
}
|
|
129
|
+
async function unbindProjectCategory(name) {
|
|
130
|
+
const project = getProjectByName(name);
|
|
131
|
+
if (!project) throw new Error(`Project not found: ${name}`);
|
|
132
|
+
delete project.discordCategoryId;
|
|
133
|
+
delete project.discordCategoryName;
|
|
134
|
+
delete project.historyChannelId;
|
|
135
|
+
delete project.controlChannelId;
|
|
136
|
+
project.updatedAt = Date.now();
|
|
137
|
+
await saveRegistry();
|
|
138
|
+
}
|
|
139
|
+
async function setProjectHistoryChannel(name, historyChannelId) {
|
|
140
|
+
const project = getProjectByName(name);
|
|
141
|
+
if (!project) throw new Error(`Project not found: ${name}`);
|
|
142
|
+
project.historyChannelId = historyChannelId;
|
|
143
|
+
project.updatedAt = Date.now();
|
|
144
|
+
await saveRegistry();
|
|
145
|
+
}
|
|
146
|
+
async function setProjectControlChannel(name, controlChannelId) {
|
|
147
|
+
const project = getProjectByName(name);
|
|
148
|
+
if (!project) throw new Error(`Project not found: ${name}`);
|
|
149
|
+
project.controlChannelId = controlChannelId;
|
|
150
|
+
project.updatedAt = Date.now();
|
|
151
|
+
await saveRegistry();
|
|
152
|
+
}
|
|
153
|
+
async function updateProject(project) {
|
|
154
|
+
const index = projects.findIndex((item) => item.name === project.name);
|
|
155
|
+
if (index < 0) throw new Error(`Project not found: ${project.name}`);
|
|
156
|
+
projects[index] = { ...project, updatedAt: Date.now() };
|
|
157
|
+
await saveRegistry();
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export {
|
|
161
|
+
Store,
|
|
162
|
+
loadRegistry,
|
|
163
|
+
getProjectByName,
|
|
164
|
+
getProjectByPath,
|
|
165
|
+
getProjectByCategoryId,
|
|
166
|
+
getAllRegisteredProjects,
|
|
167
|
+
registerProject,
|
|
168
|
+
renameProject,
|
|
169
|
+
removeProject,
|
|
170
|
+
bindProjectCategory,
|
|
171
|
+
unbindProjectCategory,
|
|
172
|
+
setProjectHistoryChannel,
|
|
173
|
+
setProjectControlChannel,
|
|
174
|
+
updateProject
|
|
175
|
+
};
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/global-config.ts
|
|
4
|
+
import Configstore from "configstore";
|
|
5
|
+
import { existsSync, readFileSync } from "fs";
|
|
6
|
+
import { join } from "path";
|
|
7
|
+
import { homedir } from "os";
|
|
8
|
+
var SENSITIVE_KEYS = /* @__PURE__ */ new Set(["DISCORD_TOKEN", "ANTHROPIC_API_KEY", "CODEX_API_KEY"]);
|
|
9
|
+
var VALID_KEYS = /* @__PURE__ */ new Set([
|
|
10
|
+
"DISCORD_TOKEN",
|
|
11
|
+
"DISCORD_CLIENT_ID",
|
|
12
|
+
"DISCORD_GUILD_ID",
|
|
13
|
+
"ALLOWED_USERS",
|
|
14
|
+
"ALLOW_ALL_USERS",
|
|
15
|
+
"DEFAULT_PROVIDER",
|
|
16
|
+
"DEFAULT_MODE",
|
|
17
|
+
"CLAUDE_PERMISSION_MODE",
|
|
18
|
+
"MAX_SUBAGENT_DEPTH",
|
|
19
|
+
"MAX_ACTIVE_SESSIONS",
|
|
20
|
+
"AUTO_ARCHIVE_DAYS",
|
|
21
|
+
"CODEX_SANDBOX_MODE",
|
|
22
|
+
"CODEX_APPROVAL_POLICY",
|
|
23
|
+
"CODEX_NETWORK_ACCESS_ENABLED",
|
|
24
|
+
"CODEX_WEB_SEARCH",
|
|
25
|
+
"CODEX_REASONING_EFFORT",
|
|
26
|
+
"CODEX_PATH",
|
|
27
|
+
"CODEX_API_KEY",
|
|
28
|
+
"CODEX_BASE_URL",
|
|
29
|
+
"ANTHROPIC_API_KEY",
|
|
30
|
+
"ANTHROPIC_BASE_URL",
|
|
31
|
+
"MESSAGE_RETENTION_DAYS",
|
|
32
|
+
"RATE_LIMIT_MS",
|
|
33
|
+
"SHELL_ENABLED",
|
|
34
|
+
"SHELL_ALLOWED_USERS",
|
|
35
|
+
"SESSION_SYNC_INTERVAL_MS",
|
|
36
|
+
"HEALTH_REPORT_ENABLED",
|
|
37
|
+
"HEALTH_REPORT_INTERVAL_MS",
|
|
38
|
+
"HEALTH_CHECK_STUCK_THRESHOLD_MS",
|
|
39
|
+
"HEALTH_CHECK_IDLE_THRESHOLD_MS"
|
|
40
|
+
]);
|
|
41
|
+
var CODEX_SANDBOX_MODES = /* @__PURE__ */ new Set(["read-only", "workspace-write", "danger-full-access"]);
|
|
42
|
+
var CODEX_APPROVAL_POLICIES = /* @__PURE__ */ new Set(["never", "on-request", "on-failure", "untrusted"]);
|
|
43
|
+
var store = null;
|
|
44
|
+
function getStore() {
|
|
45
|
+
if (!store) {
|
|
46
|
+
store = new Configstore("workspacecord", {}, { globalConfigPath: true });
|
|
47
|
+
}
|
|
48
|
+
return store;
|
|
49
|
+
}
|
|
50
|
+
function validateConfigValue(key, value) {
|
|
51
|
+
if (!VALID_KEYS.has(key)) {
|
|
52
|
+
return `Unknown config key: ${key}. Valid keys: ${Array.from(VALID_KEYS).join(", ")}`;
|
|
53
|
+
}
|
|
54
|
+
switch (key) {
|
|
55
|
+
case "CODEX_SANDBOX_MODE":
|
|
56
|
+
if (!CODEX_SANDBOX_MODES.has(value)) {
|
|
57
|
+
return `Invalid value for CODEX_SANDBOX_MODE. Expected one of: ${Array.from(CODEX_SANDBOX_MODES).join(", ")}`;
|
|
58
|
+
}
|
|
59
|
+
break;
|
|
60
|
+
case "CODEX_APPROVAL_POLICY":
|
|
61
|
+
if (!CODEX_APPROVAL_POLICIES.has(value)) {
|
|
62
|
+
return `Invalid value for CODEX_APPROVAL_POLICY. Expected one of: ${Array.from(CODEX_APPROVAL_POLICIES).join(", ")}`;
|
|
63
|
+
}
|
|
64
|
+
break;
|
|
65
|
+
case "ALLOW_ALL_USERS":
|
|
66
|
+
if (value !== "true" && value !== "false") {
|
|
67
|
+
return `Invalid value for ALLOW_ALL_USERS. Expected "true" or "false"`;
|
|
68
|
+
}
|
|
69
|
+
break;
|
|
70
|
+
case "SHELL_ENABLED":
|
|
71
|
+
if (value !== "true" && value !== "false") {
|
|
72
|
+
return `Invalid value for SHELL_ENABLED. Expected "true" or "false"`;
|
|
73
|
+
}
|
|
74
|
+
break;
|
|
75
|
+
case "RATE_LIMIT_MS":
|
|
76
|
+
case "SESSION_SYNC_INTERVAL_MS":
|
|
77
|
+
case "HEALTH_REPORT_INTERVAL_MS":
|
|
78
|
+
case "HEALTH_CHECK_STUCK_THRESHOLD_MS":
|
|
79
|
+
case "HEALTH_CHECK_IDLE_THRESHOLD_MS": {
|
|
80
|
+
const n = Number(value);
|
|
81
|
+
if (!Number.isInteger(n) || n < 0) {
|
|
82
|
+
return `Invalid value for ${key}. Expected a non-negative integer`;
|
|
83
|
+
}
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
case "HEALTH_REPORT_ENABLED":
|
|
87
|
+
if (value !== "true" && value !== "false") {
|
|
88
|
+
return `Invalid value for HEALTH_REPORT_ENABLED. Expected "true" or "false"`;
|
|
89
|
+
}
|
|
90
|
+
break;
|
|
91
|
+
case "MAX_SUBAGENT_DEPTH":
|
|
92
|
+
case "MAX_ACTIVE_SESSIONS": {
|
|
93
|
+
const n = Number(value);
|
|
94
|
+
if (!Number.isInteger(n) || n <= 0) {
|
|
95
|
+
return `Invalid value for ${key}. Expected a positive integer`;
|
|
96
|
+
}
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
case "MESSAGE_RETENTION_DAYS": {
|
|
100
|
+
const n = Number(value);
|
|
101
|
+
if (!Number.isInteger(n) || n <= 0) {
|
|
102
|
+
return `Invalid value for MESSAGE_RETENTION_DAYS. Expected a positive integer`;
|
|
103
|
+
}
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
case "AUTO_ARCHIVE_DAYS": {
|
|
107
|
+
const n = Number(value);
|
|
108
|
+
if (!Number.isInteger(n) || n < 0) {
|
|
109
|
+
return `Invalid value for AUTO_ARCHIVE_DAYS. Expected a non-negative integer`;
|
|
110
|
+
}
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
case "DEFAULT_PROVIDER":
|
|
114
|
+
if (!["claude", "codex"].includes(value)) {
|
|
115
|
+
return `Invalid value for DEFAULT_PROVIDER. Expected one of: claude, codex`;
|
|
116
|
+
}
|
|
117
|
+
break;
|
|
118
|
+
case "DEFAULT_MODE":
|
|
119
|
+
if (!["auto", "plan", "normal", "monitor"].includes(value)) {
|
|
120
|
+
return `Invalid value for DEFAULT_MODE. Expected one of: auto, plan, normal, monitor`;
|
|
121
|
+
}
|
|
122
|
+
break;
|
|
123
|
+
case "CLAUDE_PERMISSION_MODE":
|
|
124
|
+
if (!["bypass", "normal"].includes(value)) {
|
|
125
|
+
return `Invalid value for CLAUDE_PERMISSION_MODE. Expected one of: bypass, normal`;
|
|
126
|
+
}
|
|
127
|
+
break;
|
|
128
|
+
case "CODEX_WEB_SEARCH":
|
|
129
|
+
if (!["disabled", "cached", "live"].includes(value)) {
|
|
130
|
+
return `Invalid value for CODEX_WEB_SEARCH. Expected one of: disabled, cached, live`;
|
|
131
|
+
}
|
|
132
|
+
break;
|
|
133
|
+
case "CODEX_REASONING_EFFORT":
|
|
134
|
+
if (!["", "minimal", "low", "medium", "high", "xhigh"].includes(value)) {
|
|
135
|
+
return `Invalid value for CODEX_REASONING_EFFORT. Expected one of: minimal, low, medium, high, xhigh`;
|
|
136
|
+
}
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
function maskSensitive(key, value) {
|
|
142
|
+
if (!SENSITIVE_KEYS.has(key)) return value;
|
|
143
|
+
if (value.length <= 8) return "********";
|
|
144
|
+
return `${value.slice(0, 4)}********${value.slice(-4)}`;
|
|
145
|
+
}
|
|
146
|
+
function readAnthropicConfig(key) {
|
|
147
|
+
const path = join(homedir(), ".claude", "settings.json");
|
|
148
|
+
try {
|
|
149
|
+
if (existsSync(path)) {
|
|
150
|
+
const content = readFileSync(path, "utf-8");
|
|
151
|
+
const config = JSON.parse(content);
|
|
152
|
+
const env = config.env || {};
|
|
153
|
+
if (key === "ANTHROPIC_API_KEY") {
|
|
154
|
+
return env.ANTHROPIC_AUTH_TOKEN || env.ANTHROPIC_API_KEY;
|
|
155
|
+
}
|
|
156
|
+
if (key === "ANTHROPIC_BASE_URL") {
|
|
157
|
+
return env.ANTHROPIC_BASE_URL;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
} catch {
|
|
161
|
+
}
|
|
162
|
+
return void 0;
|
|
163
|
+
}
|
|
164
|
+
function readCodexConfig(key) {
|
|
165
|
+
const path = join(homedir(), ".codex", "config.json");
|
|
166
|
+
try {
|
|
167
|
+
if (existsSync(path)) {
|
|
168
|
+
const content = readFileSync(path, "utf-8");
|
|
169
|
+
const config = JSON.parse(content);
|
|
170
|
+
if (key === "CODEX_API_KEY" && config.api_key) {
|
|
171
|
+
return config.api_key;
|
|
172
|
+
}
|
|
173
|
+
if (key === "CODEX_BASE_URL" && config.base_url) {
|
|
174
|
+
return config.base_url;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
} catch {
|
|
178
|
+
}
|
|
179
|
+
return void 0;
|
|
180
|
+
}
|
|
181
|
+
function getConfigValue(key) {
|
|
182
|
+
const storeValue = getStore().get(key);
|
|
183
|
+
if (storeValue !== void 0) {
|
|
184
|
+
return storeValue;
|
|
185
|
+
}
|
|
186
|
+
const envValue = process.env[key];
|
|
187
|
+
if (envValue !== void 0) {
|
|
188
|
+
return envValue;
|
|
189
|
+
}
|
|
190
|
+
if (key === "ANTHROPIC_API_KEY" || key === "ANTHROPIC_BASE_URL") {
|
|
191
|
+
return readAnthropicConfig(key);
|
|
192
|
+
}
|
|
193
|
+
if (key === "CODEX_API_KEY" || key === "CODEX_BASE_URL") {
|
|
194
|
+
return readCodexConfig(key);
|
|
195
|
+
}
|
|
196
|
+
return void 0;
|
|
197
|
+
}
|
|
198
|
+
function setConfigValue(key, value) {
|
|
199
|
+
getStore().set(key, value);
|
|
200
|
+
}
|
|
201
|
+
function deleteConfigValue(key) {
|
|
202
|
+
getStore().delete(key);
|
|
203
|
+
}
|
|
204
|
+
function getAllConfig() {
|
|
205
|
+
return getStore().all ?? {};
|
|
206
|
+
}
|
|
207
|
+
function getConfigPath() {
|
|
208
|
+
return getStore().path;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export {
|
|
212
|
+
SENSITIVE_KEYS,
|
|
213
|
+
VALID_KEYS,
|
|
214
|
+
validateConfigValue,
|
|
215
|
+
maskSensitive,
|
|
216
|
+
getConfigValue,
|
|
217
|
+
setConfigValue,
|
|
218
|
+
deleteConfigValue,
|
|
219
|
+
getAllConfig,
|
|
220
|
+
getConfigPath
|
|
221
|
+
};
|