amalgm 0.0.0 → 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/README.md +37 -1
- package/bin/amalgm.js +8 -2
- package/lib/auth-store.js +223 -0
- package/lib/cli.js +1000 -0
- package/lib/paths.js +30 -0
- package/lib/supervisor.js +467 -0
- package/lib/tunnel-chat.js +328 -0
- package/lib/tunnel-events.js +499 -0
- package/package.json +29 -3
- package/runtime/README.md +4 -0
- package/runtime/lib/chatInput.js +306 -0
- package/runtime/lib/harnesses.js +988 -0
- package/runtime/lib/local/amalgmStore.js +128 -0
- package/runtime/lib/local/credentialResolver.js +425 -0
- package/runtime/lib/mcpApps/registry.js +619 -0
- package/runtime/package.json +5 -0
- package/runtime/scripts/amalgm-mcp/agents/rest.js +165 -0
- package/runtime/scripts/amalgm-mcp/agents/store.js +153 -0
- package/runtime/scripts/amalgm-mcp/agents/talk.js +1156 -0
- package/runtime/scripts/amalgm-mcp/agents/tools.js +210 -0
- package/runtime/scripts/amalgm-mcp/artifacts/advertise.js +132 -0
- package/runtime/scripts/amalgm-mcp/artifacts/rest.js +103 -0
- package/runtime/scripts/amalgm-mcp/artifacts/store.js +141 -0
- package/runtime/scripts/amalgm-mcp/artifacts/supervisor.js +402 -0
- package/runtime/scripts/amalgm-mcp/artifacts/tools.js +176 -0
- package/runtime/scripts/amalgm-mcp/browser/page.js +637 -0
- package/runtime/scripts/amalgm-mcp/browser/tools.js +688 -0
- package/runtime/scripts/amalgm-mcp/config.js +138 -0
- package/runtime/scripts/amalgm-mcp/credentials/rest.js +45 -0
- package/runtime/scripts/amalgm-mcp/deps.js +40 -0
- package/runtime/scripts/amalgm-mcp/email/inbound.js +215 -0
- package/runtime/scripts/amalgm-mcp/events/executor.js +179 -0
- package/runtime/scripts/amalgm-mcp/events/ingress.js +113 -0
- package/runtime/scripts/amalgm-mcp/events/matcher.js +125 -0
- package/runtime/scripts/amalgm-mcp/events/rest.js +200 -0
- package/runtime/scripts/amalgm-mcp/events/ring-buffer.js +19 -0
- package/runtime/scripts/amalgm-mcp/events/store.js +98 -0
- package/runtime/scripts/amalgm-mcp/events/tools.js +306 -0
- package/runtime/scripts/amalgm-mcp/events/webhook-url.js +28 -0
- package/runtime/scripts/amalgm-mcp/fs/rest.js +293 -0
- package/runtime/scripts/amalgm-mcp/index.js +100 -0
- package/runtime/scripts/amalgm-mcp/lib/chat-runner.js +167 -0
- package/runtime/scripts/amalgm-mcp/lib/email-md.js +288 -0
- package/runtime/scripts/amalgm-mcp/lib/mcp-resolver.js +63 -0
- package/runtime/scripts/amalgm-mcp/lib/prefs.js +393 -0
- package/runtime/scripts/amalgm-mcp/lib/storage.js +92 -0
- package/runtime/scripts/amalgm-mcp/lib/supabase.js +118 -0
- package/runtime/scripts/amalgm-mcp/lib/tool-result.js +177 -0
- package/runtime/scripts/amalgm-mcp/local/rest.js +80 -0
- package/runtime/scripts/amalgm-mcp/mcp-connections/rest.js +151 -0
- package/runtime/scripts/amalgm-mcp/notify/index.js +107 -0
- package/runtime/scripts/amalgm-mcp/server/http.js +335 -0
- package/runtime/scripts/amalgm-mcp/server/mcp.js +116 -0
- package/runtime/scripts/amalgm-mcp/slack/inbound.js +200 -0
- package/runtime/scripts/amalgm-mcp/tasks/executor.js +225 -0
- package/runtime/scripts/amalgm-mcp/tasks/rest.js +110 -0
- package/runtime/scripts/amalgm-mcp/tasks/schedule-normalization.js +85 -0
- package/runtime/scripts/amalgm-mcp/tasks/scheduler.js +105 -0
- package/runtime/scripts/amalgm-mcp/tasks/store.js +139 -0
- package/runtime/scripts/amalgm-mcp/tasks/tools.js +391 -0
- package/runtime/scripts/amalgm-mcp/user-api-keys/rest.js +105 -0
- package/runtime/scripts/amalgm-mcp/workspace/rest.js +389 -0
- package/runtime/scripts/chat-core/adapters/claude.js +163 -0
- package/runtime/scripts/chat-core/adapters/codex.js +313 -0
- package/runtime/scripts/chat-core/adapters/opencode.js +412 -0
- package/runtime/scripts/chat-core/auth.js +177 -0
- package/runtime/scripts/chat-core/contract.js +326 -0
- package/runtime/scripts/chat-core/credentials/store.js +212 -0
- package/runtime/scripts/chat-core/egress.js +87 -0
- package/runtime/scripts/chat-core/engine.js +195 -0
- package/runtime/scripts/chat-core/event-schema.js +231 -0
- package/runtime/scripts/chat-core/events.js +190 -0
- package/runtime/scripts/chat-core/index.js +11 -0
- package/runtime/scripts/chat-core/input.js +50 -0
- package/runtime/scripts/chat-core/normalizers/claude.js +450 -0
- package/runtime/scripts/chat-core/normalizers/codex.js +380 -0
- package/runtime/scripts/chat-core/normalizers/normalizer_spec.md +259 -0
- package/runtime/scripts/chat-core/normalizers/opencode.js +552 -0
- package/runtime/scripts/chat-core/normalizers/tool_contract.md +123 -0
- package/runtime/scripts/chat-core/normalizers/usage_contract.md +304 -0
- package/runtime/scripts/chat-core/parts.js +253 -0
- package/runtime/scripts/chat-core/recorder.js +65 -0
- package/runtime/scripts/chat-core/runtime.js +86 -0
- package/runtime/scripts/chat-core/server.js +163 -0
- package/runtime/scripts/chat-core/sse.js +196 -0
- package/runtime/scripts/chat-core/stores.js +100 -0
- package/runtime/scripts/chat-core/tool-display.js +149 -0
- package/runtime/scripts/chat-core/tool-shape.js +143 -0
- package/runtime/scripts/chat-core/tooling/mcp-bundle.js +161 -0
- package/runtime/scripts/chat-core/tooling/mcp-relay.js +97 -0
- package/runtime/scripts/chat-core/tooling/native-binaries.js +608 -0
- package/runtime/scripts/chat-core/tooling/system-prompt.js +20 -0
- package/runtime/scripts/chat-core/usage.js +343 -0
- package/runtime/scripts/chat-server/config.js +110 -0
- package/runtime/scripts/chat-server/db.js +529 -0
- package/runtime/scripts/chat-server/index.js +33 -0
- package/runtime/scripts/chat-server/model-catalog.js +327 -0
- package/runtime/scripts/chat-server.js +75 -0
- package/runtime/scripts/credential-adapter.js +129 -0
- package/runtime/scripts/fs-watcher.js +888 -0
- package/runtime/scripts/local-gateway.js +852 -0
- package/runtime/scripts/platform-context.txt +246 -0
- package/runtime/scripts/port-monitor.js +175 -0
- package/runtime/scripts/proxy-token-store.js +162 -0
- package/runtime/scripts/runtime-auth.js +163 -0
- package/runtime/scripts/test-claude-code-models.js +87 -0
- package/runtime/tsconfig.json +15 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// lib/local/amalgmStore.ts
|
|
31
|
+
var amalgmStore_exports = {};
|
|
32
|
+
__export(amalgmStore_exports, {
|
|
33
|
+
ensureAmalgmDir: () => ensureAmalgmDir,
|
|
34
|
+
getAmalgmDir: () => getAmalgmDir,
|
|
35
|
+
getHomeDir: () => getHomeDir,
|
|
36
|
+
readResource: () => readResource,
|
|
37
|
+
writeResource: () => writeResource
|
|
38
|
+
});
|
|
39
|
+
module.exports = __toCommonJS(amalgmStore_exports);
|
|
40
|
+
var import_fs = __toESM(require("fs"));
|
|
41
|
+
var import_path = __toESM(require("path"));
|
|
42
|
+
var import_os = __toESM(require("os"));
|
|
43
|
+
var AMALGM_DIR = import_path.default.join(import_os.default.homedir(), ".amalgm");
|
|
44
|
+
var DIRECTORIES = [
|
|
45
|
+
"",
|
|
46
|
+
// ~/.amalgm/
|
|
47
|
+
"browser",
|
|
48
|
+
// ~/.amalgm/browser/
|
|
49
|
+
"browser/local-storage",
|
|
50
|
+
// ~/.amalgm/browser/local-storage/
|
|
51
|
+
"agents",
|
|
52
|
+
// ~/.amalgm/agents/
|
|
53
|
+
"instructions",
|
|
54
|
+
// ~/.amalgm/instructions/
|
|
55
|
+
".memories",
|
|
56
|
+
// ~/.amalgm/.memories/
|
|
57
|
+
"conversations",
|
|
58
|
+
// ~/.amalgm/conversations/
|
|
59
|
+
"conversations/active",
|
|
60
|
+
// ~/.amalgm/conversations/active/
|
|
61
|
+
"conversations/saved"
|
|
62
|
+
// ~/.amalgm/conversations/saved/
|
|
63
|
+
];
|
|
64
|
+
var DEFAULT_FILES = {
|
|
65
|
+
"config.json": {
|
|
66
|
+
version: 1,
|
|
67
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
68
|
+
},
|
|
69
|
+
"browser/cookies.json": {
|
|
70
|
+
version: 1,
|
|
71
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
72
|
+
cookies: []
|
|
73
|
+
},
|
|
74
|
+
"agents/discovery.json": {
|
|
75
|
+
version: 1,
|
|
76
|
+
lastScan: null,
|
|
77
|
+
discovered: []
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
function ensureAmalgmDir() {
|
|
81
|
+
let created = false;
|
|
82
|
+
for (const dir of DIRECTORIES) {
|
|
83
|
+
const fullPath = import_path.default.join(AMALGM_DIR, dir);
|
|
84
|
+
if (!import_fs.default.existsSync(fullPath)) {
|
|
85
|
+
import_fs.default.mkdirSync(fullPath, { recursive: true });
|
|
86
|
+
created = true;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
for (const [filePath, defaultContent] of Object.entries(DEFAULT_FILES)) {
|
|
90
|
+
const fullPath = import_path.default.join(AMALGM_DIR, filePath);
|
|
91
|
+
if (!import_fs.default.existsSync(fullPath)) {
|
|
92
|
+
import_fs.default.writeFileSync(fullPath, JSON.stringify(defaultContent, null, 2));
|
|
93
|
+
created = true;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return { created, path: AMALGM_DIR };
|
|
97
|
+
}
|
|
98
|
+
function readResource(resourcePath) {
|
|
99
|
+
const fullPath = import_path.default.join(AMALGM_DIR, resourcePath);
|
|
100
|
+
try {
|
|
101
|
+
const content = import_fs.default.readFileSync(fullPath, "utf-8");
|
|
102
|
+
return JSON.parse(content);
|
|
103
|
+
} catch {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function writeResource(resourcePath, data) {
|
|
108
|
+
const fullPath = import_path.default.join(AMALGM_DIR, resourcePath);
|
|
109
|
+
const dir = import_path.default.dirname(fullPath);
|
|
110
|
+
if (!import_fs.default.existsSync(dir)) {
|
|
111
|
+
import_fs.default.mkdirSync(dir, { recursive: true });
|
|
112
|
+
}
|
|
113
|
+
import_fs.default.writeFileSync(fullPath, JSON.stringify(data, null, 2));
|
|
114
|
+
}
|
|
115
|
+
function getAmalgmDir() {
|
|
116
|
+
return AMALGM_DIR;
|
|
117
|
+
}
|
|
118
|
+
function getHomeDir() {
|
|
119
|
+
return import_os.default.homedir();
|
|
120
|
+
}
|
|
121
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
122
|
+
0 && (module.exports = {
|
|
123
|
+
ensureAmalgmDir,
|
|
124
|
+
getAmalgmDir,
|
|
125
|
+
getHomeDir,
|
|
126
|
+
readResource,
|
|
127
|
+
writeResource
|
|
128
|
+
});
|
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// lib/local/credentialResolver.ts
|
|
31
|
+
var credentialResolver_exports = {};
|
|
32
|
+
__export(credentialResolver_exports, {
|
|
33
|
+
discoverAgents: () => discoverAgents,
|
|
34
|
+
getReauthCommand: () => getReauthCommand,
|
|
35
|
+
resolveAgentEnv: () => resolveAgentEnv
|
|
36
|
+
});
|
|
37
|
+
module.exports = __toCommonJS(credentialResolver_exports);
|
|
38
|
+
var import_fs = __toESM(require("fs"));
|
|
39
|
+
var import_path = __toESM(require("path"));
|
|
40
|
+
var import_os = __toESM(require("os"));
|
|
41
|
+
var import_child_process = require("child_process");
|
|
42
|
+
var CLAUDE_KEYCHAIN_SERVICE = "Claude Code-credentials";
|
|
43
|
+
var CLAUDE_KEYCHAIN_CACHE_TTL_MS = 3e4;
|
|
44
|
+
var CLAUDE_STATUS_CACHE_TTL_MS = 3e4;
|
|
45
|
+
var LOCAL_CLI_ENV_ALLOWLIST = [
|
|
46
|
+
"PATH",
|
|
47
|
+
"HOME",
|
|
48
|
+
"USER",
|
|
49
|
+
"LOGNAME",
|
|
50
|
+
"SHELL",
|
|
51
|
+
"TMPDIR",
|
|
52
|
+
"TMP",
|
|
53
|
+
"TEMP",
|
|
54
|
+
"LANG",
|
|
55
|
+
"LC_ALL",
|
|
56
|
+
"LC_CTYPE",
|
|
57
|
+
"TERM",
|
|
58
|
+
"XDG_CONFIG_HOME",
|
|
59
|
+
"XDG_CACHE_HOME",
|
|
60
|
+
"XDG_DATA_HOME"
|
|
61
|
+
];
|
|
62
|
+
var claudeCliStatusCache = null;
|
|
63
|
+
var claudeKeychainStatusCache = null;
|
|
64
|
+
function localCliEnv() {
|
|
65
|
+
const env = { NODE_ENV: process.env.NODE_ENV || "production" };
|
|
66
|
+
for (const key of LOCAL_CLI_ENV_ALLOWLIST) {
|
|
67
|
+
const value = process.env[key];
|
|
68
|
+
if (typeof value === "string" && value.length > 0) env[key] = value;
|
|
69
|
+
}
|
|
70
|
+
return env;
|
|
71
|
+
}
|
|
72
|
+
function compareVersionStringsDesc(a, b) {
|
|
73
|
+
const parse = (value) => value.split(".").map((part) => Number(part) || 0);
|
|
74
|
+
const left = parse(a);
|
|
75
|
+
const right = parse(b);
|
|
76
|
+
const length = Math.max(left.length, right.length);
|
|
77
|
+
for (let i = 0; i < length; i += 1) {
|
|
78
|
+
const delta = (right[i] || 0) - (left[i] || 0);
|
|
79
|
+
if (delta !== 0) return delta;
|
|
80
|
+
}
|
|
81
|
+
return 0;
|
|
82
|
+
}
|
|
83
|
+
function findClaudeCodeExecutable() {
|
|
84
|
+
const home = import_os.default.homedir();
|
|
85
|
+
if (process.platform === "darwin") {
|
|
86
|
+
const claudeCodeBase = import_path.default.join(home, "Library", "Application Support", "Claude", "claude-code");
|
|
87
|
+
if (import_fs.default.existsSync(claudeCodeBase)) {
|
|
88
|
+
try {
|
|
89
|
+
const versions = import_fs.default.readdirSync(claudeCodeBase).filter((dir) => /^\d+\.\d+(\.\d+)?/.test(dir)).sort(compareVersionStringsDesc);
|
|
90
|
+
for (const version of versions) {
|
|
91
|
+
const appBinary = import_path.default.join(claudeCodeBase, version, "claude.app", "Contents", "MacOS", "claude");
|
|
92
|
+
if (import_fs.default.existsSync(appBinary)) return appBinary;
|
|
93
|
+
const directBinary = import_path.default.join(claudeCodeBase, version, "claude");
|
|
94
|
+
if (import_fs.default.existsSync(directBinary)) return directBinary;
|
|
95
|
+
}
|
|
96
|
+
} catch {
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
try {
|
|
101
|
+
const resolved = (0, import_child_process.execFileSync)("which", ["claude"], {
|
|
102
|
+
encoding: "utf-8",
|
|
103
|
+
env: localCliEnv(),
|
|
104
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
105
|
+
}).trim();
|
|
106
|
+
return resolved || null;
|
|
107
|
+
} catch {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function readClaudeCliStatus() {
|
|
112
|
+
if (claudeCliStatusCache && claudeCliStatusCache.expiresAt > Date.now()) {
|
|
113
|
+
return claudeCliStatusCache.result;
|
|
114
|
+
}
|
|
115
|
+
const executable = findClaudeCodeExecutable();
|
|
116
|
+
if (!executable) {
|
|
117
|
+
const result = { known: false, status: { hasAuth: false } };
|
|
118
|
+
claudeCliStatusCache = {
|
|
119
|
+
expiresAt: Date.now() + CLAUDE_STATUS_CACHE_TTL_MS,
|
|
120
|
+
result
|
|
121
|
+
};
|
|
122
|
+
return result;
|
|
123
|
+
}
|
|
124
|
+
try {
|
|
125
|
+
const raw = (0, import_child_process.execFileSync)(executable, ["auth", "status"], {
|
|
126
|
+
encoding: "utf-8",
|
|
127
|
+
env: localCliEnv(),
|
|
128
|
+
timeout: 5e3,
|
|
129
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
130
|
+
}).trim();
|
|
131
|
+
const parsed = JSON.parse(raw);
|
|
132
|
+
const isLoggedIn = parsed?.loggedIn === true;
|
|
133
|
+
const authMethod = typeof parsed?.authMethod === "string" ? parsed.authMethod : "configured";
|
|
134
|
+
const subscriptionType = typeof parsed?.subscriptionType === "string" ? parsed.subscriptionType : "";
|
|
135
|
+
const hint = subscriptionType ? `${authMethod}:${subscriptionType}` : authMethod;
|
|
136
|
+
const result = {
|
|
137
|
+
known: true,
|
|
138
|
+
status: {
|
|
139
|
+
hasAuth: isLoggedIn,
|
|
140
|
+
authDetails: { type: "oauth", hint }
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
claudeCliStatusCache = {
|
|
144
|
+
expiresAt: Date.now() + CLAUDE_STATUS_CACHE_TTL_MS,
|
|
145
|
+
result
|
|
146
|
+
};
|
|
147
|
+
return result;
|
|
148
|
+
} catch (error) {
|
|
149
|
+
const stderr = error instanceof Error ? `${error.message}` : String(error);
|
|
150
|
+
const authFailed = /401|Please run \/login|Not logged in/i.test(stderr);
|
|
151
|
+
const result = {
|
|
152
|
+
known: authFailed,
|
|
153
|
+
status: {
|
|
154
|
+
hasAuth: false,
|
|
155
|
+
authDetails: authFailed ? { type: "oauth", hint: "reauth required" } : void 0
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
claudeCliStatusCache = {
|
|
159
|
+
expiresAt: Date.now() + CLAUDE_STATUS_CACHE_TTL_MS,
|
|
160
|
+
result
|
|
161
|
+
};
|
|
162
|
+
return result;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
function readClaudeKeychainStatus() {
|
|
166
|
+
if (claudeKeychainStatusCache && claudeKeychainStatusCache.expiresAt > Date.now()) {
|
|
167
|
+
return claudeKeychainStatusCache.result;
|
|
168
|
+
}
|
|
169
|
+
let result = {
|
|
170
|
+
hasAuth: false
|
|
171
|
+
};
|
|
172
|
+
try {
|
|
173
|
+
(0, import_child_process.execFileSync)(
|
|
174
|
+
"security",
|
|
175
|
+
["find-generic-password", "-s", CLAUDE_KEYCHAIN_SERVICE, "-a", import_os.default.userInfo().username],
|
|
176
|
+
{ stdio: "ignore" }
|
|
177
|
+
);
|
|
178
|
+
result = {
|
|
179
|
+
hasAuth: true,
|
|
180
|
+
authDetails: { type: "oauth", hint: "keychain" }
|
|
181
|
+
};
|
|
182
|
+
try {
|
|
183
|
+
const raw = (0, import_child_process.execFileSync)(
|
|
184
|
+
"security",
|
|
185
|
+
["find-generic-password", "-s", CLAUDE_KEYCHAIN_SERVICE, "-a", import_os.default.userInfo().username, "-w"],
|
|
186
|
+
{ encoding: "utf-8" }
|
|
187
|
+
).trim();
|
|
188
|
+
const parsed = JSON.parse(raw);
|
|
189
|
+
const accessToken = typeof parsed?.claudeAiOauth?.accessToken === "string" ? parsed.claudeAiOauth.accessToken : "";
|
|
190
|
+
const expiresAt = Number(parsed?.claudeAiOauth?.expiresAt || 0);
|
|
191
|
+
const isExpired = expiresAt > 0 && expiresAt <= Date.now() + 6e4;
|
|
192
|
+
if (!accessToken || isExpired) {
|
|
193
|
+
result = {
|
|
194
|
+
hasAuth: false,
|
|
195
|
+
authDetails: { type: "oauth", hint: isExpired ? "expired" : "missing token" }
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
} catch {
|
|
199
|
+
}
|
|
200
|
+
} catch {
|
|
201
|
+
result = { hasAuth: false };
|
|
202
|
+
}
|
|
203
|
+
claudeKeychainStatusCache = {
|
|
204
|
+
expiresAt: Date.now() + CLAUDE_KEYCHAIN_CACHE_TTL_MS,
|
|
205
|
+
result
|
|
206
|
+
};
|
|
207
|
+
return result;
|
|
208
|
+
}
|
|
209
|
+
function detectClaudeCode() {
|
|
210
|
+
const configDir = import_path.default.join(import_os.default.homedir(), ".claude");
|
|
211
|
+
if (!import_fs.default.existsSync(configDir)) return null;
|
|
212
|
+
const entry = {
|
|
213
|
+
id: "claude_code",
|
|
214
|
+
name: "Claude Code",
|
|
215
|
+
configDir,
|
|
216
|
+
hasAuth: false
|
|
217
|
+
};
|
|
218
|
+
if (process.platform !== "darwin") {
|
|
219
|
+
return entry;
|
|
220
|
+
}
|
|
221
|
+
const cliStatus = readClaudeCliStatus();
|
|
222
|
+
if (cliStatus.known) {
|
|
223
|
+
entry.hasAuth = cliStatus.status.hasAuth;
|
|
224
|
+
entry.authDetails = cliStatus.status.authDetails;
|
|
225
|
+
return entry;
|
|
226
|
+
}
|
|
227
|
+
const keychainStatus = readClaudeKeychainStatus();
|
|
228
|
+
entry.hasAuth = keychainStatus.hasAuth;
|
|
229
|
+
entry.authDetails = keychainStatus.authDetails;
|
|
230
|
+
return entry;
|
|
231
|
+
}
|
|
232
|
+
function detectCodex() {
|
|
233
|
+
const configDir = import_path.default.join(import_os.default.homedir(), ".codex");
|
|
234
|
+
const authPath = import_path.default.join(configDir, "auth.json");
|
|
235
|
+
if (!import_fs.default.existsSync(authPath)) return null;
|
|
236
|
+
const entry = {
|
|
237
|
+
id: "codex",
|
|
238
|
+
name: "Codex",
|
|
239
|
+
configDir,
|
|
240
|
+
hasAuth: false
|
|
241
|
+
};
|
|
242
|
+
try {
|
|
243
|
+
const parsed = JSON.parse(import_fs.default.readFileSync(authPath, "utf-8"));
|
|
244
|
+
if (parsed.tokens && typeof parsed.tokens === "object") {
|
|
245
|
+
entry.hasAuth = true;
|
|
246
|
+
entry.authDetails = { type: "oauth", hint: parsed.auth_mode || "configured" };
|
|
247
|
+
} else if (typeof parsed.OPENAI_API_KEY === "string" && parsed.OPENAI_API_KEY.length > 0) {
|
|
248
|
+
entry.hasAuth = true;
|
|
249
|
+
entry.authDetails = { type: "api_key", hint: parsed.OPENAI_API_KEY.slice(-4) };
|
|
250
|
+
}
|
|
251
|
+
} catch {
|
|
252
|
+
}
|
|
253
|
+
return entry;
|
|
254
|
+
}
|
|
255
|
+
function hasNonEmptyFile(filePath) {
|
|
256
|
+
try {
|
|
257
|
+
const stat = import_fs.default.statSync(filePath);
|
|
258
|
+
return stat.isFile() && stat.size > 0;
|
|
259
|
+
} catch {
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
function firstExistingPath(paths) {
|
|
264
|
+
return paths.find((candidate) => import_fs.default.existsSync(candidate)) ?? null;
|
|
265
|
+
}
|
|
266
|
+
function detectOpenCode() {
|
|
267
|
+
const home = import_os.default.homedir();
|
|
268
|
+
const configDir = import_path.default.join(home, ".config", "opencode");
|
|
269
|
+
const authPath = import_path.default.join(home, ".local", "share", "opencode", "auth.json");
|
|
270
|
+
if (!import_fs.default.existsSync(configDir) && !import_fs.default.existsSync(authPath)) return null;
|
|
271
|
+
const hasAuth = hasNonEmptyFile(authPath);
|
|
272
|
+
return {
|
|
273
|
+
id: "opencode",
|
|
274
|
+
name: "OpenCode",
|
|
275
|
+
configDir,
|
|
276
|
+
hasAuth,
|
|
277
|
+
authDetails: hasAuth ? { type: "oauth", hint: "configured" } : void 0
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
function detectPi() {
|
|
281
|
+
const configDir = import_path.default.join(import_os.default.homedir(), ".pi");
|
|
282
|
+
const credentialPaths = [
|
|
283
|
+
import_path.default.join(configDir, "agent", "auth.json"),
|
|
284
|
+
import_path.default.join(configDir, "agent", "models.json")
|
|
285
|
+
];
|
|
286
|
+
if (!import_fs.default.existsSync(configDir) && !firstExistingPath(credentialPaths)) return null;
|
|
287
|
+
const hasAuth = credentialPaths.some(hasNonEmptyFile);
|
|
288
|
+
return {
|
|
289
|
+
id: "pi",
|
|
290
|
+
name: "Pi",
|
|
291
|
+
configDir,
|
|
292
|
+
hasAuth,
|
|
293
|
+
authDetails: hasAuth ? { type: "unknown", hint: "configured" } : void 0
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
function detectAmp() {
|
|
297
|
+
const home = import_os.default.homedir();
|
|
298
|
+
const configDir = import_path.default.join(home, ".config", "amp");
|
|
299
|
+
const credentialPaths = [
|
|
300
|
+
import_path.default.join(configDir, "settings.json"),
|
|
301
|
+
import_path.default.join(home, ".amp", "config.json"),
|
|
302
|
+
import_path.default.join(home, ".amp", "auth.json")
|
|
303
|
+
];
|
|
304
|
+
const hasEnvKey = typeof process.env.AMP_API_KEY === "string" && process.env.AMP_API_KEY.length > 0;
|
|
305
|
+
if (!hasEnvKey && !import_fs.default.existsSync(configDir) && !firstExistingPath(credentialPaths)) return null;
|
|
306
|
+
const hasAuth = hasEnvKey || credentialPaths.some(hasNonEmptyFile);
|
|
307
|
+
return {
|
|
308
|
+
id: "amp",
|
|
309
|
+
name: "Amp",
|
|
310
|
+
configDir,
|
|
311
|
+
hasAuth,
|
|
312
|
+
authDetails: hasAuth ? { type: hasEnvKey ? "api_key" : "unknown", hint: hasEnvKey ? process.env.AMP_API_KEY.slice(-4) : "configured" } : void 0
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
function detectCursor() {
|
|
316
|
+
const home = import_os.default.homedir();
|
|
317
|
+
const configDir = import_path.default.join(home, ".cursor");
|
|
318
|
+
const credentialPaths = [
|
|
319
|
+
import_path.default.join(configDir, "credentials.json"),
|
|
320
|
+
import_path.default.join(home, ".config", "cursor", "credentials.json"),
|
|
321
|
+
import_path.default.join(home, ".local", "share", "cursor-agent", "auth.json")
|
|
322
|
+
];
|
|
323
|
+
const hasEnvKey = typeof process.env.CURSOR_API_KEY === "string" && process.env.CURSOR_API_KEY.length > 0;
|
|
324
|
+
let statusAuth = false;
|
|
325
|
+
if (!hasEnvKey) {
|
|
326
|
+
const executable = firstExistingPath([
|
|
327
|
+
import_path.default.join(home, ".local", "bin", "cursor-agent"),
|
|
328
|
+
"/usr/local/bin/cursor-agent"
|
|
329
|
+
]) ?? "cursor-agent";
|
|
330
|
+
try {
|
|
331
|
+
const raw = (0, import_child_process.execFileSync)(executable, ["status"], {
|
|
332
|
+
encoding: "utf-8",
|
|
333
|
+
env: localCliEnv(),
|
|
334
|
+
timeout: 3e3,
|
|
335
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
336
|
+
});
|
|
337
|
+
statusAuth = /authenticated|logged in|signed in/i.test(raw) && !/not authenticated|logged out/i.test(raw);
|
|
338
|
+
} catch {
|
|
339
|
+
statusAuth = false;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
if (!hasEnvKey && !statusAuth && !import_fs.default.existsSync(configDir) && !firstExistingPath(credentialPaths)) return null;
|
|
343
|
+
const hasAuth = hasEnvKey || statusAuth || credentialPaths.some(hasNonEmptyFile);
|
|
344
|
+
return {
|
|
345
|
+
id: "cursor",
|
|
346
|
+
name: "Cursor",
|
|
347
|
+
configDir,
|
|
348
|
+
hasAuth,
|
|
349
|
+
authDetails: hasAuth ? { type: hasEnvKey ? "api_key" : "oauth", hint: hasEnvKey ? process.env.CURSOR_API_KEY.slice(-4) : "configured" } : void 0
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
function discoverAgents() {
|
|
353
|
+
const discovered = [];
|
|
354
|
+
for (const detector of [detectClaudeCode, detectCodex, detectOpenCode, detectPi, detectAmp, detectCursor]) {
|
|
355
|
+
const result = detector();
|
|
356
|
+
if (result) discovered.push(result);
|
|
357
|
+
}
|
|
358
|
+
return discovered;
|
|
359
|
+
}
|
|
360
|
+
function getReauthCommand(agentId) {
|
|
361
|
+
switch (agentId) {
|
|
362
|
+
case "claude_code":
|
|
363
|
+
return {
|
|
364
|
+
shell: "claude",
|
|
365
|
+
description: "Run `claude`, then type /login to authenticate."
|
|
366
|
+
};
|
|
367
|
+
case "codex":
|
|
368
|
+
return {
|
|
369
|
+
shell: "codex login",
|
|
370
|
+
description: "`codex login` walks through OAuth or API-key setup."
|
|
371
|
+
};
|
|
372
|
+
case "opencode":
|
|
373
|
+
return {
|
|
374
|
+
shell: "opencode auth login",
|
|
375
|
+
description: "`opencode auth login` walks through provider setup."
|
|
376
|
+
};
|
|
377
|
+
case "pi":
|
|
378
|
+
return {
|
|
379
|
+
shell: "pi --terminal-login",
|
|
380
|
+
description: "`pi --terminal-login` authenticates Pi in the terminal."
|
|
381
|
+
};
|
|
382
|
+
case "amp":
|
|
383
|
+
return {
|
|
384
|
+
shell: "amp --setup",
|
|
385
|
+
description: "`amp --setup` configures Amp credentials."
|
|
386
|
+
};
|
|
387
|
+
case "cursor":
|
|
388
|
+
return {
|
|
389
|
+
shell: 'if command -v cursor-agent >/dev/null 2>&1; then cursor-agent login; else "$HOME/.local/bin/cursor-agent" login; fi',
|
|
390
|
+
description: "`cursor-agent login` opens the browser login flow."
|
|
391
|
+
};
|
|
392
|
+
default:
|
|
393
|
+
return null;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
function resolveAgentEnv(agentId, authMode, proxyToken, proxyBaseUrl) {
|
|
397
|
+
const env = {};
|
|
398
|
+
if (authMode === "amalgm" && proxyToken && proxyBaseUrl) {
|
|
399
|
+
switch (agentId) {
|
|
400
|
+
case "claude_code":
|
|
401
|
+
env.ANTHROPIC_API_KEY = proxyToken;
|
|
402
|
+
env.ANTHROPIC_BASE_URL = `${proxyBaseUrl}/anthropic`;
|
|
403
|
+
break;
|
|
404
|
+
case "codex":
|
|
405
|
+
env.OPENAI_API_KEY = proxyToken;
|
|
406
|
+
env.OPENAI_BASE_URL = `${proxyBaseUrl}/openai/v1`;
|
|
407
|
+
break;
|
|
408
|
+
case "opencode":
|
|
409
|
+
env.AI_GATEWAY_API_KEY = proxyToken;
|
|
410
|
+
env.AI_GATEWAY_BASE_URL = `${proxyBaseUrl}/ai_gateway/v3/ai`;
|
|
411
|
+
break;
|
|
412
|
+
case "pi":
|
|
413
|
+
env.AI_GATEWAY_API_KEY = proxyToken;
|
|
414
|
+
env.AI_GATEWAY_BASE_URL = `${proxyBaseUrl}/ai_gateway/v1`;
|
|
415
|
+
break;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
return env;
|
|
419
|
+
}
|
|
420
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
421
|
+
0 && (module.exports = {
|
|
422
|
+
discoverAgents,
|
|
423
|
+
getReauthCommand,
|
|
424
|
+
resolveAgentEnv
|
|
425
|
+
});
|