amalgm 0.1.66 → 0.1.68
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/package.json
CHANGED
|
@@ -126,11 +126,6 @@ function runtimeHome({ amalgmDir, harness, authProfileId }) {
|
|
|
126
126
|
return path.join(amalgmDir, 'cli-homes', safePathSegment(harness), safePathSegment(authProfileId));
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
-
function nativeProviderHome(harness) {
|
|
130
|
-
if (harness === 'claude_code') return os.homedir();
|
|
131
|
-
return null;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
129
|
function pinnedRuntimeHome(amalgmDir, cliHomePath) {
|
|
135
130
|
if (typeof cliHomePath !== 'string' || !cliHomePath.trim()) return null;
|
|
136
131
|
const root = path.resolve(amalgmDir);
|
|
@@ -183,8 +178,7 @@ function authEnvelope({ harness, authMethod, sessionId, localBaseUrl, proxyToken
|
|
|
183
178
|
tokenFingerprint,
|
|
184
179
|
authProfileId: profileId,
|
|
185
180
|
runtimeHome:
|
|
186
|
-
|
|
187
|
-
|| pinnedRuntimeHome(amalgmDir, cliHomePath)
|
|
181
|
+
pinnedRuntimeHome(amalgmDir, cliHomePath)
|
|
188
182
|
|| runtimeHome({ amalgmDir, harness, authProfileId: profileId }),
|
|
189
183
|
};
|
|
190
184
|
}
|
|
@@ -202,15 +196,6 @@ function runtimeEnv(contract, baseEnv = process.env) {
|
|
|
202
196
|
for (const key of ['OPENAI_API_KEY', 'OPENAI_BASE_URL', 'ANTHROPIC_API_KEY', 'ANTHROPIC_BASE_URL', 'AI_GATEWAY_API_KEY', 'AI_GATEWAY_BASE_URL']) {
|
|
203
197
|
delete env[key];
|
|
204
198
|
}
|
|
205
|
-
if (contract.harness === 'claude_code' && contract.authMethod === 'provider_auth') {
|
|
206
|
-
env.HOME = os.homedir();
|
|
207
|
-
delete env.CLAUDE_CONFIG_DIR;
|
|
208
|
-
return {
|
|
209
|
-
...env,
|
|
210
|
-
IS_SANDBOX: '1',
|
|
211
|
-
...(baseEnv.AMALGM_RUNTIME_TOKEN ? { AMALGM_RUNTIME_TOKEN: baseEnv.AMALGM_RUNTIME_TOKEN } : {}),
|
|
212
|
-
};
|
|
213
|
-
}
|
|
214
199
|
if (contract.auth.runtimeHome) {
|
|
215
200
|
fs.mkdirSync(contract.auth.runtimeHome, { recursive: true });
|
|
216
201
|
env.HOME = contract.auth.runtimeHome;
|
|
@@ -218,6 +203,9 @@ function runtimeEnv(contract, baseEnv = process.env) {
|
|
|
218
203
|
env.CLAUDE_CONFIG_DIR = contract.auth.runtimeHome;
|
|
219
204
|
env.OPENCODE_HOME = contract.auth.runtimeHome;
|
|
220
205
|
env.OPENCODE_CONFIG_DIR = contract.auth.runtimeHome;
|
|
206
|
+
if (contract.harness === 'claude_code' && contract.authMethod === 'provider_auth') {
|
|
207
|
+
delete env.CLAUDE_CONFIG_DIR;
|
|
208
|
+
}
|
|
221
209
|
}
|
|
222
210
|
env.IS_SANDBOX = '1';
|
|
223
211
|
if (baseEnv.AMALGM_RUNTIME_TOKEN) env.AMALGM_RUNTIME_TOKEN = baseEnv.AMALGM_RUNTIME_TOKEN;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const assert = require('node:assert/strict');
|
|
4
|
-
const os = require('node:os');
|
|
5
4
|
const test = require('node:test');
|
|
6
5
|
const { authEnvelope, runtimeEnv } = require('../auth');
|
|
7
6
|
|
|
@@ -63,7 +62,7 @@ test('opencode amalgm auth uses a pinned CLI home across sessions and proxy toke
|
|
|
63
62
|
assert.equal(env.OPENCODE_CONFIG_DIR, first.runtimeHome);
|
|
64
63
|
});
|
|
65
64
|
|
|
66
|
-
test('claude provider auth uses
|
|
65
|
+
test('claude provider auth uses a pinned CLI home with native config copied separately', () => {
|
|
67
66
|
const envelope = authEnvelope({
|
|
68
67
|
harness: 'claude_code',
|
|
69
68
|
authMethod: 'provider_auth',
|
|
@@ -71,7 +70,7 @@ test('claude provider auth uses the native user home with CLAUDE_CONFIG_DIR unse
|
|
|
71
70
|
amalgmDir: '/tmp/amalgm-test',
|
|
72
71
|
});
|
|
73
72
|
|
|
74
|
-
assert.
|
|
73
|
+
assert.match(envelope.runtimeHome, /\/tmp\/amalgm-test\/cli-homes\/claude_code\/provider-[0-9a-f]{16}$/);
|
|
75
74
|
|
|
76
75
|
const env = runtimeEnv({
|
|
77
76
|
harness: 'claude_code',
|
|
@@ -84,7 +83,7 @@ test('claude provider auth uses the native user home with CLAUDE_CONFIG_DIR unse
|
|
|
84
83
|
ANTHROPIC_API_KEY: 'should-not-leak',
|
|
85
84
|
});
|
|
86
85
|
|
|
87
|
-
assert.equal(env.HOME,
|
|
86
|
+
assert.equal(env.HOME, envelope.runtimeHome);
|
|
88
87
|
assert.equal(env.CLAUDE_CONFIG_DIR, undefined);
|
|
89
88
|
assert.equal(env.ANTHROPIC_API_KEY, undefined);
|
|
90
89
|
});
|
|
@@ -30,7 +30,7 @@ function withNativeHome(fn) {
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
test('codex native sync copies
|
|
33
|
+
test('codex native sync copies config without deleting existing runtime state', () => {
|
|
34
34
|
withNativeHome((home) => {
|
|
35
35
|
const source = path.join(home, '.codex');
|
|
36
36
|
fs.mkdirSync(path.join(source, 'sessions'), { recursive: true });
|
|
@@ -58,7 +58,8 @@ test('codex native sync copies hook support without bulk runtime state', () => {
|
|
|
58
58
|
assert.equal(fs.existsSync(path.join(runtimeHome, 'hooks.json')), true);
|
|
59
59
|
assert.equal(fs.existsSync(path.join(runtimeHome, 'supermemory.json')), true);
|
|
60
60
|
assert.equal(fs.existsSync(path.join(runtimeHome, 'supermemory', 'recall.js')), true);
|
|
61
|
-
assert.equal(fs.existsSync(path.join(runtimeHome, 'sessions')),
|
|
61
|
+
assert.equal(fs.existsSync(path.join(runtimeHome, 'sessions', 'legacy.jsonl')), true);
|
|
62
|
+
assert.equal(fs.existsSync(path.join(runtimeHome, 'sessions', 'huge.jsonl')), false);
|
|
62
63
|
assert.equal(fs.existsSync(path.join(runtimeHome, 'worktrees')), false);
|
|
63
64
|
assert.equal(fs.existsSync(path.join(runtimeHome, 'plugins')), false);
|
|
64
65
|
assert.equal(fs.existsSync(path.join(runtimeHome, '.codex', 'hooks.json')), true);
|
|
@@ -195,3 +196,23 @@ test('claude native sync skips debug symlinks', () => {
|
|
|
195
196
|
assert.equal(fs.existsSync(path.join(runtimeHome, 'debug', 'latest')), false);
|
|
196
197
|
});
|
|
197
198
|
});
|
|
199
|
+
|
|
200
|
+
test('claude native sync links native keychains on macOS', () => {
|
|
201
|
+
withNativeHome((home) => {
|
|
202
|
+
const source = path.join(home, '.claude');
|
|
203
|
+
fs.mkdirSync(source, { recursive: true });
|
|
204
|
+
fs.writeFileSync(path.join(source, 'settings.json'), '{"hooks":{}}');
|
|
205
|
+
fs.mkdirSync(path.join(home, 'Library', 'Keychains'), { recursive: true });
|
|
206
|
+
|
|
207
|
+
const runtimeHome = path.join(home, 'runtime-home');
|
|
208
|
+
syncClaudeNativeConfig(runtimeHome);
|
|
209
|
+
|
|
210
|
+
const keychains = path.join(runtimeHome, 'Library', 'Keychains');
|
|
211
|
+
if (process.platform === 'darwin') {
|
|
212
|
+
assert.equal(fs.lstatSync(keychains).isSymbolicLink(), true);
|
|
213
|
+
assert.equal(fs.realpathSync(keychains), fs.realpathSync(path.join(home, 'Library', 'Keychains')));
|
|
214
|
+
} else {
|
|
215
|
+
assert.equal(fs.existsSync(keychains), false);
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
});
|
|
@@ -120,16 +120,30 @@ function removeOutboundSymlink(root, relativePath) {
|
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
function
|
|
124
|
-
|
|
123
|
+
function ensureNativeKeychainAlias(runtimeHome) {
|
|
124
|
+
if (process.platform !== 'darwin' || !runtimeHome) return false;
|
|
125
|
+
const source = path.join(nativeHome(), 'Library', 'Keychains');
|
|
126
|
+
if (!exists(source)) return false;
|
|
127
|
+
const target = path.join(runtimeHome, 'Library', 'Keychains');
|
|
128
|
+
try {
|
|
129
|
+
const stat = fs.lstatSync(target);
|
|
130
|
+
if (stat.isSymbolicLink()) {
|
|
131
|
+
const linkTarget = fs.readlinkSync(target);
|
|
132
|
+
if (path.resolve(path.dirname(target), linkTarget) === path.resolve(source)) return true;
|
|
133
|
+
}
|
|
134
|
+
fs.rmSync(target, { recursive: true, force: true });
|
|
135
|
+
} catch {}
|
|
136
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
137
|
+
try {
|
|
138
|
+
fs.symlinkSync(source, target, 'dir');
|
|
139
|
+
return true;
|
|
140
|
+
} catch {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
125
143
|
}
|
|
126
144
|
|
|
127
|
-
function
|
|
128
|
-
|
|
129
|
-
try {
|
|
130
|
-
fs.rmSync(path.join(runtimeHome, name), { recursive: true, force: true });
|
|
131
|
-
} catch {}
|
|
132
|
-
}
|
|
145
|
+
function nativeHome() {
|
|
146
|
+
return process.env.AMALGM_NATIVE_HOME || os.homedir();
|
|
133
147
|
}
|
|
134
148
|
|
|
135
149
|
function copyDirBounded(sourceDir, targetDir, options = {}) {
|
|
@@ -178,7 +192,6 @@ function syncCodexNativeConfig(runtimeHome) {
|
|
|
178
192
|
fs.mkdirSync(runtimeHome, { recursive: true });
|
|
179
193
|
if (!exists(sourceDir)) return null;
|
|
180
194
|
|
|
181
|
-
pruneLegacyCodexRuntimeHome(runtimeHome);
|
|
182
195
|
const nativeConfig = copyDirBounded(sourceDir, runtimeHome);
|
|
183
196
|
const copiedFiles = [
|
|
184
197
|
copyFileIfPresent(path.join(sourceDir, 'config.toml'), path.join(runtimeHome, 'config.toml')),
|
|
@@ -208,6 +221,7 @@ function syncClaudeNativeConfig(runtimeHome) {
|
|
|
208
221
|
removeOutboundSymlink(runtimeHome, path.join('debug', 'latest'));
|
|
209
222
|
const copied = copyConfigTree(sourceDir, runtimeHome);
|
|
210
223
|
ensureHomeAlias(runtimeHome, '.claude');
|
|
224
|
+
ensureNativeKeychainAlias(runtimeHome);
|
|
211
225
|
copyFileIfPresent(path.join(home, '.claude.json'), path.join(runtimeHome, '.claude.json'));
|
|
212
226
|
copyConfigTree(path.join(home, '.config', 'claude'), path.join(runtimeHome, '.config', 'claude'));
|
|
213
227
|
return copied ? { sourceDir, runtimeHome } : null;
|
|
@@ -268,6 +282,7 @@ module.exports = {
|
|
|
268
282
|
copyConfigTree,
|
|
269
283
|
copyDirBounded,
|
|
270
284
|
copyFileIfPresent,
|
|
285
|
+
ensureNativeKeychainAlias,
|
|
271
286
|
ensureHomeAlias,
|
|
272
287
|
shouldCopyConfigPath,
|
|
273
288
|
},
|