@offerpilot/axiomruntime 0.0.1 → 0.0.3
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/README.md +18 -11
- package/dist/cli/commands/set.js +121 -0
- package/dist/cli/commands/telegram.js +4 -3
- package/dist/cli/index.js +11 -1
- package/dist/cli/sanitize-args.js +6 -0
- package/dist/core/integrations/codex-provider-config.js +540 -0
- package/dist/core/runner/claude-permission-policy.js +19 -0
- package/dist/core/runner/engine-registry.js +4 -4
- package/dist/core/runner/tool-runner.js +46 -9
- package/dist/core/status/doctor-service.js +17 -0
- package/dist/telegram/bot-registry.js +17 -4
- package/dist/telegram/engine/claude-engine.js +23 -6
- package/dist/telegram/engine/codex-engine.js +0 -2
- package/dist/telegram/engine/engine-utils.js +14 -3
- package/docs/USAGE.html +41 -9
- package/package.json +6 -5
|
@@ -7,6 +7,7 @@ import { writeLog } from "../logs/log-service.js";
|
|
|
7
7
|
import { isRecord } from "../utils/is-record.js";
|
|
8
8
|
import { resolveToolCommand } from "./command-resolver.js";
|
|
9
9
|
import { getRunnerEngine } from "./engine-registry.js";
|
|
10
|
+
import { CLAUDE_ROOT_PERMISSION_WARNING, resolveClaudePermissionMode } from "./claude-permission-policy.js";
|
|
10
11
|
import { finishSession, startSession } from "../sessions/session-service.js";
|
|
11
12
|
export async function runTool(tool, provider, model, options = {}) {
|
|
12
13
|
await saveLastSelection(tool, provider.name, model);
|
|
@@ -15,13 +16,20 @@ export async function runTool(tool, provider, model, options = {}) {
|
|
|
15
16
|
throw new Error(`Failed to find ${tool} CLI. Run \`ai doctor\` to check local CLI installation.`);
|
|
16
17
|
}
|
|
17
18
|
const engine = getRunnerEngine(tool);
|
|
18
|
-
const
|
|
19
|
+
const launchPolicy = resolveRunnerLaunchPolicy(tool, provider);
|
|
20
|
+
if (launchPolicy.warning) {
|
|
21
|
+
console.warn(`Warning: ${launchPolicy.warning}`);
|
|
22
|
+
}
|
|
23
|
+
const effectiveProvider = launchPolicy.provider;
|
|
24
|
+
const env = buildRunnerEnv(tool, effectiveProvider, model);
|
|
19
25
|
const temporaryFiles = [];
|
|
20
|
-
const claudeSettingsPath = engine.requiresSettingsFile
|
|
26
|
+
const claudeSettingsPath = engine.requiresSettingsFile
|
|
27
|
+
? writeTemporaryClaudeSettings(effectiveProvider, { permissionMode: launchPolicy.claudeSettingsPermissionMode })
|
|
28
|
+
: null;
|
|
21
29
|
if (claudeSettingsPath)
|
|
22
30
|
temporaryFiles.push(claudeSettingsPath);
|
|
23
|
-
const args = buildRunnerArgs(tool,
|
|
24
|
-
const effectiveBaseUrl = getToolBaseUrl(tool,
|
|
31
|
+
const args = buildRunnerArgs(tool, effectiveProvider, model, claudeSettingsPath);
|
|
32
|
+
const effectiveBaseUrl = getToolBaseUrl(tool, effectiveProvider);
|
|
25
33
|
await writeLog({
|
|
26
34
|
category: "usage",
|
|
27
35
|
action: "tool_spawn",
|
|
@@ -30,7 +38,9 @@ export async function runTool(tool, provider, model, options = {}) {
|
|
|
30
38
|
tool,
|
|
31
39
|
provider: provider.name,
|
|
32
40
|
model,
|
|
33
|
-
mode:
|
|
41
|
+
mode: effectiveProvider.mode,
|
|
42
|
+
configuredMode: provider.mode,
|
|
43
|
+
permissionConstraint: launchPolicy.reason,
|
|
34
44
|
baseUrl: effectiveBaseUrl,
|
|
35
45
|
configuredBaseUrl: provider.baseUrl,
|
|
36
46
|
apiKeyFingerprint: getSecretFingerprint(provider.apiKey),
|
|
@@ -41,7 +51,7 @@ export async function runTool(tool, provider, model, options = {}) {
|
|
|
41
51
|
});
|
|
42
52
|
const session = await startSession({
|
|
43
53
|
tool,
|
|
44
|
-
provider,
|
|
54
|
+
provider: effectiveProvider,
|
|
45
55
|
model,
|
|
46
56
|
projectPath: process.cwd(),
|
|
47
57
|
fallbackFrom: options.fallbackFrom ?? null
|
|
@@ -70,8 +80,27 @@ export function getSecretFingerprint(value) {
|
|
|
70
80
|
return `len=${value.length}`;
|
|
71
81
|
return `${value.slice(0, 8)}...${value.slice(-6)} len=${value.length}`;
|
|
72
82
|
}
|
|
73
|
-
export function buildRunnerArgs(tool, provider, model, claudeSettingsPath) {
|
|
74
|
-
|
|
83
|
+
export function buildRunnerArgs(tool, provider, model, claudeSettingsPath, identity = {}) {
|
|
84
|
+
const launchPolicy = resolveRunnerLaunchPolicy(tool, provider, identity);
|
|
85
|
+
return getRunnerEngine(tool).buildArgs({
|
|
86
|
+
provider: launchPolicy.provider,
|
|
87
|
+
model,
|
|
88
|
+
settingsPath: claudeSettingsPath,
|
|
89
|
+
uid: identity.uid
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
export function resolveRunnerLaunchPolicy(tool, provider, identity = {}) {
|
|
93
|
+
if (tool !== "claude") {
|
|
94
|
+
return { provider, claudeSettingsPermissionMode: "provider", warning: null, reason: null };
|
|
95
|
+
}
|
|
96
|
+
const requestedMode = provider.mode === "auto" ? "bypassPermissions" : "default";
|
|
97
|
+
const resolution = resolveClaudePermissionMode(requestedMode, identity);
|
|
98
|
+
return {
|
|
99
|
+
provider: resolution.constrained ? { ...provider, mode: "ask" } : provider,
|
|
100
|
+
claudeSettingsPermissionMode: resolution.privileged ? "safeDefault" : "provider",
|
|
101
|
+
warning: resolution.constrained ? CLAUDE_ROOT_PERMISSION_WARNING : null,
|
|
102
|
+
reason: resolution.reason
|
|
103
|
+
};
|
|
75
104
|
}
|
|
76
105
|
const AUTO_PERMISSIONS = {
|
|
77
106
|
defaultMode: "bypassPermissions",
|
|
@@ -94,6 +123,11 @@ const READ_ONLY_PERMISSIONS = {
|
|
|
94
123
|
allow: ["Read", "Glob", "Grep", "WebFetch", "WebSearch"],
|
|
95
124
|
deny: ["Bash", "Edit", "Write", "MultiEdit", "NotebookEdit"]
|
|
96
125
|
};
|
|
126
|
+
const SAFE_DEFAULT_PERMISSIONS = {
|
|
127
|
+
defaultMode: "default",
|
|
128
|
+
allow: [],
|
|
129
|
+
deny: []
|
|
130
|
+
};
|
|
97
131
|
export function writeTemporaryClaudeSettings(provider, options = {}) {
|
|
98
132
|
const userSettingsPath = path.join(os.homedir(), ".claude", "settings.json");
|
|
99
133
|
const settings = readClaudeSettings(userSettingsPath);
|
|
@@ -104,7 +138,10 @@ export function writeTemporaryClaudeSettings(provider, options = {}) {
|
|
|
104
138
|
ANTHROPIC_API_KEY: provider.apiKey,
|
|
105
139
|
ANTHROPIC_BASE_URL: baseUrl
|
|
106
140
|
};
|
|
107
|
-
if (options.permissionMode === "
|
|
141
|
+
if (options.permissionMode === "safeDefault") {
|
|
142
|
+
settings.permissions = { ...SAFE_DEFAULT_PERMISSIONS };
|
|
143
|
+
}
|
|
144
|
+
else if (options.permissionMode === "readOnly") {
|
|
108
145
|
settings.permissions = { ...READ_ONLY_PERMISSIONS };
|
|
109
146
|
}
|
|
110
147
|
else if (provider.mode === "auto") {
|
|
@@ -8,6 +8,7 @@ import { readProviders } from "../config/providers-store.js";
|
|
|
8
8
|
import { checkProvider, runDeepCheck } from "../models/model-discovery.js";
|
|
9
9
|
import { writeLog } from "../logs/log-service.js";
|
|
10
10
|
import { getKnownCandidates, resolveToolCommand } from "../runner/command-resolver.js";
|
|
11
|
+
import { isPrivilegedProcess } from "../runner/claude-permission-policy.js";
|
|
11
12
|
import { resolveProviderCandidatesForTool } from "../runner/fallback.js";
|
|
12
13
|
import { buildUsageEvent } from "../usage/usage-service.js";
|
|
13
14
|
const MIN_NODE_VERSION = "22.19.0";
|
|
@@ -16,6 +17,7 @@ export async function runDoctor(options = {}) {
|
|
|
16
17
|
const fix = options.fix ?? false;
|
|
17
18
|
const checkProviders = options.checkProviders ?? true;
|
|
18
19
|
checkNodeVersion(items);
|
|
20
|
+
checkExecutionUser(items);
|
|
19
21
|
if (options.checkNativeDependencies ?? true) {
|
|
20
22
|
await checkNativeDependencies(items);
|
|
21
23
|
}
|
|
@@ -26,6 +28,21 @@ export async function runDoctor(options = {}) {
|
|
|
26
28
|
checkCommand("Local CLIs", "codex", items);
|
|
27
29
|
return items;
|
|
28
30
|
}
|
|
31
|
+
function checkExecutionUser(items) {
|
|
32
|
+
const item = getExecutionUserDoctorItem();
|
|
33
|
+
if (item)
|
|
34
|
+
items.push(item);
|
|
35
|
+
}
|
|
36
|
+
export function getExecutionUserDoctorItem(identity = {}) {
|
|
37
|
+
if (!isPrivilegedProcess(identity))
|
|
38
|
+
return null;
|
|
39
|
+
return {
|
|
40
|
+
group: "Execution User",
|
|
41
|
+
status: "warn",
|
|
42
|
+
message: "Axiom Runtime is running as root; Claude auto/bypass permission mode is unavailable and will be constrained to default permissions.",
|
|
43
|
+
suggestion: "Install globally as root if required, but run `ai` and long-lived services with a dedicated non-root account."
|
|
44
|
+
};
|
|
45
|
+
}
|
|
29
46
|
export async function runDeepDoctor(tool = "codex", options = {}) {
|
|
30
47
|
const items = await runDoctor(options);
|
|
31
48
|
try {
|
|
@@ -3,6 +3,7 @@ import fsPromises from "node:fs/promises";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { getConfigRoot, getTelegramBotConfigPath, getTelegramBotDbPath, getTelegramBotDir, getTelegramBotLogPath, getTelegramBotPidPath, getTelegramConfigPath, getTelegramDbPath } from "../core/config/paths.js";
|
|
5
5
|
const BOT_NAME_RE = /^[a-z0-9]([a-z0-9_-]*[a-z0-9])?$/;
|
|
6
|
+
const LEGACY_MIGRATION_MARKER = ".telegram-legacy-migrated";
|
|
6
7
|
export function isValidBotName(name) {
|
|
7
8
|
return BOT_NAME_RE.test(name) && name.length <= 32;
|
|
8
9
|
}
|
|
@@ -42,13 +43,19 @@ export async function migrateLegacyConfig() {
|
|
|
42
43
|
const legacyPath = getTelegramConfigPath();
|
|
43
44
|
if (!fs.existsSync(legacyPath))
|
|
44
45
|
return;
|
|
45
|
-
if (
|
|
46
|
+
if (fs.existsSync(getLegacyMigrationMarkerPath()))
|
|
46
47
|
return;
|
|
47
48
|
const dir = getTelegramBotDir();
|
|
48
49
|
await fsPromises.mkdir(dir, { recursive: true });
|
|
49
|
-
|
|
50
|
+
if (!botExists("default")) {
|
|
51
|
+
await fsPromises.copyFile(legacyPath, getTelegramBotConfigPath("default"));
|
|
52
|
+
}
|
|
53
|
+
await markLegacyMigrationComplete();
|
|
50
54
|
}
|
|
51
55
|
export async function removeBotFiles(name, configuredDbPath) {
|
|
56
|
+
if (name === "default") {
|
|
57
|
+
await markLegacyMigrationComplete();
|
|
58
|
+
}
|
|
52
59
|
const managedConfiguredDbPath = configuredDbPath && isManagedConfigPath(configuredDbPath)
|
|
53
60
|
? path.resolve(configuredDbPath)
|
|
54
61
|
: undefined;
|
|
@@ -61,8 +68,7 @@ export async function removeBotFiles(name, configuredDbPath) {
|
|
|
61
68
|
getTelegramBotConfigPath(name),
|
|
62
69
|
getTelegramBotPidPath(name),
|
|
63
70
|
getTelegramBotLogPath(name),
|
|
64
|
-
...[...dbPaths].flatMap((dbPath) => [dbPath, `${dbPath}-shm`, `${dbPath}-wal`])
|
|
65
|
-
...(name === "default" ? [getTelegramConfigPath()] : [])
|
|
71
|
+
...[...dbPaths].flatMap((dbPath) => [dbPath, `${dbPath}-shm`, `${dbPath}-wal`])
|
|
66
72
|
];
|
|
67
73
|
for (const p of paths) {
|
|
68
74
|
try {
|
|
@@ -78,3 +84,10 @@ function isManagedConfigPath(filePath) {
|
|
|
78
84
|
const relative = path.relative(getConfigRoot(), path.resolve(filePath));
|
|
79
85
|
return relative !== "" && !relative.startsWith("..") && !path.isAbsolute(relative);
|
|
80
86
|
}
|
|
87
|
+
function getLegacyMigrationMarkerPath() {
|
|
88
|
+
return path.join(getConfigRoot(), LEGACY_MIGRATION_MARKER);
|
|
89
|
+
}
|
|
90
|
+
async function markLegacyMigrationComplete() {
|
|
91
|
+
await fsPromises.mkdir(getConfigRoot(), { recursive: true });
|
|
92
|
+
await fsPromises.writeFile(getLegacyMigrationMarkerPath(), "completed\n", { mode: 0o600 });
|
|
93
|
+
}
|
|
@@ -3,13 +3,15 @@ import { createJsonLineAccumulator, extractJsonLines, spawnCapture, stripAnsi, t
|
|
|
3
3
|
import { requiresDangerousConfirmation } from "../interaction/approval.js";
|
|
4
4
|
import { writeTelegramLog } from "../log.js";
|
|
5
5
|
import { isRecord } from "../../core/utils/is-record.js";
|
|
6
|
+
import { CLAUDE_ROOT_PERMISSION_WARNING, resolveClaudePermissionMode } from "../../core/runner/claude-permission-policy.js";
|
|
6
7
|
export class ClaudeEngine {
|
|
7
8
|
name = "claude";
|
|
8
9
|
processes = new Map();
|
|
9
10
|
controllers = new Map();
|
|
10
11
|
async execute(params) {
|
|
11
|
-
const
|
|
12
|
-
const runtime = await prepareEngineRuntime("claude", params.providerName, params.modelName,
|
|
12
|
+
const requestedPermissionMode = params.permissionMode ?? "default";
|
|
13
|
+
const runtime = await prepareEngineRuntime("claude", params.providerName, params.modelName, requestedPermissionMode);
|
|
14
|
+
const permissionMode = runtime.claudePermission?.effectiveMode ?? requestedPermissionMode;
|
|
13
15
|
const controller = new AbortController();
|
|
14
16
|
this.controllers.set(params.chatId, controller);
|
|
15
17
|
const signal = params.signal ?? controller.signal;
|
|
@@ -29,11 +31,25 @@ export class ClaudeEngine {
|
|
|
29
31
|
resume: params.resume,
|
|
30
32
|
allowedTools: params.allowedTools
|
|
31
33
|
});
|
|
32
|
-
|
|
34
|
+
const permissionWarning = runtime.claudePermission?.constrained ? CLAUDE_ROOT_PERMISSION_WARNING : null;
|
|
35
|
+
await params.onProgress?.({ phase: "thinking", message: permissionWarning ?? "Claude started" });
|
|
36
|
+
if (permissionWarning) {
|
|
37
|
+
await writeTelegramLog({
|
|
38
|
+
level: "warn",
|
|
39
|
+
action: "claude_permission_constrained",
|
|
40
|
+
message: permissionWarning,
|
|
41
|
+
metadata: { requestedPermissionMode, effectivePermissionMode: permissionMode, reason: runtime.claudePermission?.reason }
|
|
42
|
+
});
|
|
43
|
+
}
|
|
33
44
|
await writeTelegramLog({
|
|
34
45
|
action: "claude_start",
|
|
35
46
|
message: `Starting Claude for Telegram chat ${params.chatId}`,
|
|
36
|
-
metadata: {
|
|
47
|
+
metadata: {
|
|
48
|
+
...summarizeRuntime(runtime.provider, runtime.model, "claude"),
|
|
49
|
+
cwd: params.cwd,
|
|
50
|
+
requestedPermissionMode,
|
|
51
|
+
effectivePermissionMode: permissionMode
|
|
52
|
+
}
|
|
37
53
|
});
|
|
38
54
|
try {
|
|
39
55
|
const progressParser = createJsonLineAccumulator((event) => handleClaudeProgressEvent(event, params.onProgress));
|
|
@@ -89,7 +105,8 @@ export class ClaudeEngine {
|
|
|
89
105
|
}
|
|
90
106
|
}
|
|
91
107
|
export function buildClaudeArgs(options) {
|
|
92
|
-
const
|
|
108
|
+
const resolution = resolveClaudePermissionMode(options.permissionMode, { uid: options.uid });
|
|
109
|
+
const permissionMode = resolution.effectiveMode === "readOnly" ? "default" : resolution.effectiveMode;
|
|
93
110
|
const args = [
|
|
94
111
|
"-p",
|
|
95
112
|
options.prompt,
|
|
@@ -111,7 +128,7 @@ export function buildClaudeArgs(options) {
|
|
|
111
128
|
if (options.allowedTools?.length) {
|
|
112
129
|
args.push("--allowedTools", options.allowedTools.join(","));
|
|
113
130
|
}
|
|
114
|
-
if (
|
|
131
|
+
if (resolution.effectiveMode === "bypassPermissions") {
|
|
115
132
|
args.push("--dangerously-skip-permissions");
|
|
116
133
|
}
|
|
117
134
|
if (options.permissionMode === "readOnly") {
|
|
@@ -169,8 +169,6 @@ function buildCodexProviderOverrideArgs(providerOverride) {
|
|
|
169
169
|
"-c",
|
|
170
170
|
`model_providers.${providerId}.env_key=${tomlString("OPENAI_API_KEY")}`,
|
|
171
171
|
"-c",
|
|
172
|
-
`model_providers.${providerId}.requires_openai_auth=true`,
|
|
173
|
-
"-c",
|
|
174
172
|
`model_providers.${providerId}.wire_api=${tomlString("responses")}`
|
|
175
173
|
];
|
|
176
174
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { resolveExactProviderForTool, resolveProviderForTool } from "../../core/runner/fallback.js";
|
|
3
3
|
import { buildRunnerEnv, cleanupTemporaryFiles, getToolBaseUrl, writeTemporaryClaudeSettings } from "../../core/runner/tool-runner.js";
|
|
4
|
+
import { resolveClaudePermissionMode } from "../../core/runner/claude-permission-policy.js";
|
|
4
5
|
import { resolveToolCommand } from "../../core/runner/command-resolver.js";
|
|
5
6
|
export async function prepareEngineRuntime(tool, providerName, modelName, permissionMode) {
|
|
6
7
|
// Telegram resolves and orders fallback candidates before invoking an
|
|
@@ -13,17 +14,27 @@ export async function prepareEngineRuntime(tool, providerName, modelName, permis
|
|
|
13
14
|
if (!command) {
|
|
14
15
|
throw new Error(`Failed to find ${tool} CLI. Run \`ai doctor\` to check local CLI installation.`);
|
|
15
16
|
}
|
|
17
|
+
const claudePermission = tool === "claude"
|
|
18
|
+
? resolveClaudePermissionMode(permissionMode ?? "default")
|
|
19
|
+
: null;
|
|
20
|
+
const effectiveProvider = claudePermission?.constrained ? { ...provider, mode: "ask" } : provider;
|
|
16
21
|
const env = {
|
|
17
|
-
...buildRunnerEnv(tool,
|
|
22
|
+
...buildRunnerEnv(tool, effectiveProvider, model),
|
|
18
23
|
AI_GATEWAY_SOURCE: "telegram"
|
|
19
24
|
};
|
|
20
25
|
const temporaryFiles = [];
|
|
21
26
|
const claudeSettingsPath = tool === "claude"
|
|
22
|
-
? writeTemporaryClaudeSettings(provider, {
|
|
27
|
+
? writeTemporaryClaudeSettings(provider, {
|
|
28
|
+
permissionMode: permissionMode === "readOnly"
|
|
29
|
+
? "readOnly"
|
|
30
|
+
: claudePermission?.privileged
|
|
31
|
+
? "safeDefault"
|
|
32
|
+
: "provider"
|
|
33
|
+
})
|
|
23
34
|
: null;
|
|
24
35
|
if (claudeSettingsPath)
|
|
25
36
|
temporaryFiles.push(claudeSettingsPath);
|
|
26
|
-
return { provider, model, env, command, temporaryFiles, claudeSettingsPath };
|
|
37
|
+
return { provider, model, env, command, temporaryFiles, claudeSettingsPath, claudePermission };
|
|
27
38
|
}
|
|
28
39
|
export function cleanupRuntime(runtime) {
|
|
29
40
|
cleanupTemporaryFiles(runtime.temporaryFiles);
|
package/docs/USAGE.html
CHANGED
|
@@ -458,13 +458,16 @@
|
|
|
458
458
|
<section id="install" class="panel">
|
|
459
459
|
<h2>通过 npm 安装</h2>
|
|
460
460
|
<div class="detail-body">
|
|
461
|
-
<pre>
|
|
461
|
+
<pre>node --version
|
|
462
|
+
npm install --global @offerpilot/axiomruntime
|
|
462
463
|
ai --version
|
|
463
464
|
ai setup</pre>
|
|
464
465
|
<p>npm 包名是 <code>@offerpilot/axiomruntime</code>,安装后注册的可执行命令是 <code>ai</code>。需要 Node.js <code>22.19.0+</code>。</p>
|
|
466
|
+
<p>如果 npm 显示 <code>EBADENGINE</code>,请先升级 Node.js 再重新安装,不要继续使用警告状态下安装的 CLI。</p>
|
|
465
467
|
<p><code>ai --version</code> 无副作用地验证安装;<code>ai setup</code> 初始化 Runtime,并可在确认后安装缺失的 Claude Code/Codex。</p>
|
|
466
|
-
<
|
|
467
|
-
brew
|
|
468
|
+
<p>全局安装可以由 root 完成,但日常执行和常驻服务应使用专用非 root 用户。root 下请求 Claude <code>auto</code>/<code>bypassPermissions</code> 时,Runtime 会安全约束为默认权限并告警,不会传递 <code>--dangerously-skip-permissions</code>。</p>
|
|
469
|
+
<pre>brew tap linlangli/axiom https://gitlab.com/linlangli/axiom.git
|
|
470
|
+
brew install linlangli/axiom/axiomruntime
|
|
468
471
|
ai --version
|
|
469
472
|
ai setup</pre>
|
|
470
473
|
<p>Homebrew Formula 复用同版本 npm tarball并校验 SHA256,固定使用 Homebrew <code>node@22</code>。非 GitHub Tap 首次添加时需提供公开 Git URL。</p>
|
|
@@ -534,6 +537,12 @@ ai setup</pre>
|
|
|
534
537
|
<td>指定 provider 和模型启动工具。</td>
|
|
535
538
|
<td>provider 不可用时按 level 降级。</td>
|
|
536
539
|
</tr>
|
|
540
|
+
<tr data-search="set codex chatgpt login oauth provider proxy config auth restart app">
|
|
541
|
+
<td><span class="group-label">使用</span></td>
|
|
542
|
+
<td><code class="cmd">ai set codex chatgpt</code><br /><code class="cmd">ai set codex provider 51talk-cq gpt-5.6-sol</code><br /><code class="cmd">ai set codex provider 51talk-cq</code><br /><code class="cmd">ai set codex provider</code><br /><code class="cmd">ai set codex proxy 127.0.0.1:7890</code></td>
|
|
543
|
+
<td>持久切换 Codex App 的 ChatGPT 账号、已添加 provider 或 HTTP proxy。</td>
|
|
544
|
+
<td>一致更新配置与必要环境,成功后重启 app。</td>
|
|
545
|
+
</tr>
|
|
537
546
|
<tr data-search="last resume previous selection">
|
|
538
547
|
<td><span class="group-label">使用</span></td>
|
|
539
548
|
<td><code class="cmd">ai last</code></td>
|
|
@@ -556,7 +565,7 @@ ai setup</pre>
|
|
|
556
565
|
<td><span class="group-label">Provider</span></td>
|
|
557
566
|
<td><code class="cmd">ai provider edit</code><br /><code class="cmd">ai provider edit 51talk</code></td>
|
|
558
567
|
<td>修改 name、baseUrl、凭证、mode、level、model。</td>
|
|
559
|
-
<td
|
|
568
|
+
<td>当前 apiKey 以明文保存在 providers.json,编辑时会回显当前值。SecretStore + apiKeyRef 是目标态,尚未实现。</td>
|
|
560
569
|
</tr>
|
|
561
570
|
<tr data-search="provider delete remove fallback">
|
|
562
571
|
<td><span class="group-label">Provider</span></td>
|
|
@@ -628,13 +637,35 @@ ai use codex 51talk gpt-5.5</pre>
|
|
|
628
637
|
<ul>
|
|
629
638
|
<li>provider 不可用时按 <code>level</code> 自动降级。</li>
|
|
630
639
|
<li>Claude Code 使用临时 <code>--settings</code> 注入环境变量,不修改用户 settings。</li>
|
|
631
|
-
<li>
|
|
632
|
-
<li>provider 的 <code>mode</code> 为 <code>auto</code>
|
|
640
|
+
<li><code>ai use codex</code> 使用本次子进程的 <code>-c model_provider=...</code> 固定所选 provider,并通过 <code>env_key = "OPENAI_API_KEY"</code> 使用 AI Gateway 注入的 Key;不会修改 Codex 全局认证或配置。</li>
|
|
641
|
+
<li>provider 的 <code>mode</code> 为 <code>auto</code> 时,非 root 执行 Claude Code 会追加 <code>--dangerously-skip-permissions</code>;<code>ask</code> 模式不会追加,并会在启动前确认。root 下请求 <code>auto</code> 会被安全约束为默认权限,临时 settings 中继承的 bypass 配置也会被清除。</li>
|
|
633
642
|
<li>OpenAI 兼容地址用于 Codex 时会使用 <code>/v1</code>,用于 Claude 时会去掉 <code>/v1</code>。</li>
|
|
634
643
|
<li>日志只记录 Key 指纹,不记录完整 API Key。</li>
|
|
635
644
|
</ul>
|
|
636
645
|
</div>
|
|
637
646
|
</div>
|
|
647
|
+
<details open>
|
|
648
|
+
<summary>持久设置 Codex App provider</summary>
|
|
649
|
+
<div class="detail-body">
|
|
650
|
+
<pre>ai set codex chatgpt
|
|
651
|
+
ai set codex provider 51talk-cq gpt-5.6-sol
|
|
652
|
+
ai set codex provider 51talk-cq
|
|
653
|
+
ai set codex provider
|
|
654
|
+
ai set codex proxy 127.0.0.1:7890
|
|
655
|
+
ai set codex proxy off</pre>
|
|
656
|
+
<ul>
|
|
657
|
+
<li><code>chatgpt</code> 调用 <code>codex login</code> 打开浏览器,并在成功后选择内建 <code>model_provider = "openai"</code>。</li>
|
|
658
|
+
<li><code>provider name model-id</code> 显式选择该 provider 已探测的 <code>gpt-*</code> 模型;只给 provider 时自动选最新 GPT。</li>
|
|
659
|
+
<li><code>provider</code> 不带名称时依次交互选择已添加的 provider 和 GPT 模型;没有可用 GPT 时提示运行 <code>ai status</code>,不修改配置。</li>
|
|
660
|
+
<li>自动选择先比较 GPT 数字版本,同版本优先 <code>sol</code>、<code>pro</code>、alias、<code>codex</code>、<code>terra</code>、<code>mini</code>、<code>luna</code>、<code>nano</code>,并优先滚动 alias。API Key 只写入 mode <code>0600</code> 的 <code>auth.json</code>。</li>
|
|
661
|
+
<li>原有 project、MCP、plugin、feature 设置会保留;任一步失败会恢复原 <code>config.toml</code> 和 <code>auth.json</code>。</li>
|
|
662
|
+
<li>切换自定义 provider 会替换当前 ChatGPT token;切回 ChatGPT 时需重新登录。</li>
|
|
663
|
+
<li>成功后 macOS 会重启 <code>Codex.app</code> 或当前名称的 <code>ChatGPT.app</code>;无法自动重启时按提示手动操作。</li>
|
|
664
|
+
<li><code>proxy</code> 把裸 <code>host:port</code> 规范化为 HTTP origin,只更新 <code>shell_environment_policy.set</code> 的大小写 HTTP(S)/ALL proxy keys;<code>off</code> 清除这些 managed keys,不修改 <code>auth.json</code>。</li>
|
|
665
|
+
<li>Proxy URL 禁止包含账号密码、path、query 或 fragment,command usage log 会整体脱敏。macOS 会同步当前登录会话的 user launchd environment;失败时恢复原 config 且不重启,注销或重启 macOS 后需重新运行本命令。</li>
|
|
666
|
+
</ul>
|
|
667
|
+
</div>
|
|
668
|
+
</details>
|
|
638
669
|
</section>
|
|
639
670
|
|
|
640
671
|
<section id="memory" class="panel">
|
|
@@ -712,7 +743,7 @@ ai telegram status --all</pre>
|
|
|
712
743
|
<li><code>--bot=<name></code> 与 <code>--bot <name></code> 等价。旧位置参数写法继续兼容;新脚本推荐显式指定 <code>--bot</code> 或 <code>--all</code>。</li>
|
|
713
744
|
<li>裸 <code>ai telegram</code> 会先选操作再列出 Bot;生命周期菜单可选 <code>all bots</code>,删除菜单会二次确认。带 <code>--bot</code> 进入交互菜单时会跳过 Bot 选择。</li>
|
|
714
745
|
<li><code>edit</code> 交互式编辑指定 Bot;<code>delete</code> 先停止目标 Bot,再删除该 Bot 的 config、PID、log 和 SQLite 文件。旧 <code>remove</code> 是兼容别名。</li>
|
|
715
|
-
<li
|
|
746
|
+
<li>legacy <code>telegram.json</code> 首次会自动迁移为命名 Bot <code>default</code>;删除 <code>default</code> 后,列表、生命周期命令和 Bot 服务不会再从 legacy 配置重建它。无目标 <code>ai telegram config</code> 仍可管理 legacy global config。</li>
|
|
716
747
|
<li><code>config set ... --bot <name></code> 只修改指定 Bot,不影响全局或其他 Bot;token 不会出现在命令日志或终端输出中。</li>
|
|
717
748
|
<li>命名 Bot 的配置、PID、日志和会话保存在 <code>~/.ai-gateway/telegram/bots/<name>.json|.pid|.log|.sqlite</code>。</li>
|
|
718
749
|
<li>为保持兼容,存在命名 Bot 时,无目标 <code>start</code>、<code>stop</code>、<code>status</code> 仍作用于全部命名 Bot。</li>
|
|
@@ -722,7 +753,7 @@ ai telegram status --all</pre>
|
|
|
722
753
|
<li><code>/provider</code> 以按钮列出全部 AI Provider,可编辑名称、Base URL、API Key、默认模型、模式和优先级,二次确认后删除,或从列表底部添加。操作复用 <code>ai provider</code> 的核心 CRUD 和检查逻辑。</li>
|
|
723
754
|
<li>Provider API Key 在详情中只显示“已配置”,不会写入 Bot 应用日志;输入后 Bot 会尽力立即删除该 Telegram 消息。未完成表单在 Bot 重启后需要重新开始。</li>
|
|
724
755
|
<li>Telegram Codex 执行会自动走本地 OpenAI 兼容代理记录 provider usage;<code>/usage</code> 有 provider cost 时直接展示,否则按 <code>model-pricing.json</code> 估算。</li>
|
|
725
|
-
<li>如果本机不能直连 <code>api.telegram.org</code>,设置 <code>proxyUrl</code> 或环境变量 <code>HTTPS_PROXY</code> / <code>ALL_PROXY</code>。</li>
|
|
756
|
+
<li>如果本机不能直连 <code>api.telegram.org</code>,设置 <code>proxyUrl</code> 或环境变量 <code>HTTPS_PROXY</code> / <code>ALL_PROXY</code>。交互式新增或编辑 Bot 时,<code>proxyUrl</code> 输入提示会展示完整 URL 示例;地址请包含协议,例如 <code>http://127.0.0.1:7890</code>。</li>
|
|
726
757
|
</ul>
|
|
727
758
|
</div>
|
|
728
759
|
</details>
|
|
@@ -809,7 +840,8 @@ ai session clear</pre>
|
|
|
809
840
|
<pre>~/.ai-gateway/providers.json
|
|
810
841
|
~/.ai-gateway/memory.json
|
|
811
842
|
~/.ai-gateway/model-pricing.json</pre>
|
|
812
|
-
<p><code>providers.json</code> 保存聊天 provider
|
|
843
|
+
<p><code>providers.json</code> 保存聊天 provider;<code>memory.json</code> 保存记忆检索 embedding 模型。<code>model-pricing.json</code> 保存美元 / 百万 token 的模型价格。</p>
|
|
844
|
+
<p><strong>注意:</strong>这两个文件当前都以明文保存 <code>apiKey</code>。请限制文件权限并且不要提交到仓库。凭证改由 SecretStore 管理、配置只留 <code>apiKeyRef</code> 是目标架构,尚未实现。</p>
|
|
813
845
|
</div>
|
|
814
846
|
</details>
|
|
815
847
|
<details>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@offerpilot/axiomruntime",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "CLI-first foundation for an enterprise AI Runtime with provider routing and governed execution.",
|
|
5
5
|
"homepage": "https://gitlab.com/linlangli/agent-router",
|
|
6
6
|
"repository": {
|
|
@@ -22,10 +22,11 @@
|
|
|
22
22
|
"telegram:service": "node --enable-source-maps dist/telegram/supervisor.js",
|
|
23
23
|
"test": "npm run build && node --test test/*.test.js",
|
|
24
24
|
"release": "node scripts/release.js",
|
|
25
|
-
"release:current": "node scripts/release.js --
|
|
26
|
-
"release:check": "npm test && npm run
|
|
25
|
+
"release:current": "node scripts/release.js --npm",
|
|
26
|
+
"release:check": "npm test && npm pack --dry-run",
|
|
27
27
|
"homebrew:update": "node scripts/update-homebrew-formula.js --write",
|
|
28
28
|
"homebrew:check": "node scripts/update-homebrew-formula.js",
|
|
29
|
+
"homebrew:publish": "node scripts/release.js --homebrew",
|
|
29
30
|
"prepublishOnly": "npm run release:check"
|
|
30
31
|
},
|
|
31
32
|
"files": [
|
|
@@ -47,11 +48,11 @@
|
|
|
47
48
|
"typescript": "^5.8.0"
|
|
48
49
|
},
|
|
49
50
|
"dependencies": {
|
|
50
|
-
"better-sqlite3": "^
|
|
51
|
+
"better-sqlite3": "^13.0.2",
|
|
51
52
|
"gpt-tokenizer": "^3.4.0",
|
|
52
53
|
"grammy": "^1.44.0",
|
|
53
54
|
"https-proxy-agent": "^9.1.0",
|
|
54
55
|
"proper-lockfile": "^4.1.2",
|
|
55
|
-
"undici": "^8.
|
|
56
|
+
"undici": "^8.10.0"
|
|
56
57
|
}
|
|
57
58
|
}
|