sleev 0.0.10 → 0.0.11

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.
@@ -1,8 +1,12 @@
1
1
  // Claude Code setup owns global settings patching for the Anthropic-compatible proxy.
2
- import { mkdir, readFile, writeFile } from 'node:fs/promises';
2
+ import { chmod, mkdir, readFile, writeFile } from 'node:fs/promises';
3
3
  import { homedir } from 'node:os';
4
4
  import { dirname, join } from 'node:path';
5
5
  import { field, success } from '../terminal.js';
6
+ const BASE = 'ANTHROPIC_BASE_URL';
7
+ const CUSTOM = 'ANTHROPIC_CUSTOM_HEADERS';
8
+ const HARNESS = 'claude code';
9
+ const OLD_HARNESS = 'claude-code';
6
10
  export async function setupClaude(auth, path = claudePath()) {
7
11
  const result = await patchClaude(auth, path);
8
12
  success(process.stdout, 'Configured Claude Code');
@@ -12,8 +16,7 @@ export async function setupClaude(auth, path = claudePath()) {
12
16
  export async function patchClaude(auth, path = claudePath()) {
13
17
  const existing = await readJson(path);
14
18
  const config = patchConfig(existing, auth);
15
- await mkdir(dirname(path), { recursive: true, mode: 0o700 });
16
- await writeFile(path, `${JSON.stringify(config, null, 2)}\n`, { mode: 0o600 });
19
+ await writeJson(path, config);
17
20
  return { path };
18
21
  }
19
22
  export async function unpatchClaude(path = claudePath()) {
@@ -23,23 +26,22 @@ export async function unpatchClaude(path = claudePath()) {
23
26
  const env = object(existing.env);
24
27
  if (typeof env.ANTHROPIC_CUSTOM_HEADERS !== 'string')
25
28
  return;
26
- if (!env.ANTHROPIC_CUSTOM_HEADERS.includes('sleeve-harness: claude-code'))
29
+ if (!patched(env))
27
30
  return;
28
- const { ANTHROPIC_BASE_URL: _base, ANTHROPIC_CUSTOM_HEADERS: _headers, ...rest } = env;
29
- await mkdir(dirname(path), { recursive: true, mode: 0o700 });
30
- await writeFile(path, `${JSON.stringify({ ...existing, env: rest }, null, 2)}\n`, { mode: 0o600 });
31
+ await writeJson(path, removeBackup({ ...existing, env: restoreEnv(env, backup(existing)) }));
31
32
  }
32
33
  export function claudePath(env = process.env) {
33
34
  return join(env.CLAUDE_CONFIG_DIR || join(homedir(), '.claude'), 'settings.json');
34
35
  }
35
36
  function patchConfig(input, auth) {
36
37
  const env = object(input.env);
38
+ const previous = backup(input) ?? backupEnv(env);
37
39
  return {
38
- ...input,
40
+ ...withBackup(input, previous),
39
41
  env: {
40
42
  ...env,
41
- ANTHROPIC_BASE_URL: trim(auth.proxyUrl),
42
- ANTHROPIC_CUSTOM_HEADERS: headers(auth),
43
+ [BASE]: trim(auth.proxyUrl),
44
+ [CUSTOM]: headers(auth),
43
45
  },
44
46
  };
45
47
  }
@@ -47,9 +49,68 @@ function headers(auth) {
47
49
  return [
48
50
  `sleeve-token: ${auth.token}`,
49
51
  'sleeve-provider: anthropic',
50
- 'sleeve-harness: claude-code',
52
+ `sleeve-harness: ${HARNESS}`,
51
53
  ].join('\n');
52
54
  }
55
+ function backup(input) {
56
+ const sleev = object(input.sleev);
57
+ const claude = object(sleev.claudeCode);
58
+ if (!('previousEnv' in claude))
59
+ return null;
60
+ return object(claude.previousEnv);
61
+ }
62
+ function backupEnv(env) {
63
+ if (patched(env))
64
+ return {};
65
+ const previous = {};
66
+ if (BASE in env)
67
+ previous[BASE] = env[BASE];
68
+ if (CUSTOM in env)
69
+ previous[CUSTOM] = env[CUSTOM];
70
+ return previous;
71
+ }
72
+ function withBackup(input, previous) {
73
+ const sleev = object(input.sleev);
74
+ return {
75
+ ...input,
76
+ sleev: {
77
+ ...sleev,
78
+ claudeCode: {
79
+ previousEnv: previous,
80
+ },
81
+ },
82
+ };
83
+ }
84
+ function removeBackup(input) {
85
+ const sleev = object(input.sleev);
86
+ const { claudeCode: _claude, ...rest } = sleev;
87
+ if (Object.keys(rest).length > 0)
88
+ return { ...input, sleev: rest };
89
+ const { sleev: _sleev, ...config } = input;
90
+ return config;
91
+ }
92
+ function restoreEnv(env, previous) {
93
+ const { [BASE]: _base, [CUSTOM]: _headers, ...rest } = env;
94
+ const restored = { ...rest };
95
+ if (!previous)
96
+ return restored;
97
+ if (BASE in previous)
98
+ restored[BASE] = previous[BASE];
99
+ if (CUSTOM in previous)
100
+ restored[CUSTOM] = previous[CUSTOM];
101
+ return restored;
102
+ }
103
+ function patched(env) {
104
+ const value = env[CUSTOM];
105
+ if (typeof value !== 'string')
106
+ return false;
107
+ return value.includes(`sleeve-harness: ${HARNESS}`) || value.includes(`sleeve-harness: ${OLD_HARNESS}`);
108
+ }
109
+ async function writeJson(path, config) {
110
+ await mkdir(dirname(path), { recursive: true, mode: 0o700 });
111
+ await writeFile(path, `${JSON.stringify(config, null, 2)}\n`, { mode: 0o600 });
112
+ await chmod(path, 0o600);
113
+ }
53
114
  async function readJson(path) {
54
115
  try {
55
116
  const raw = await readFile(path, 'utf8');
@@ -48,7 +48,7 @@ export async function loginCommand(options = {}, runtime = commandRuntime()) {
48
48
  success(runtime.output, useLocal ? 'Already logged in' : 'Logged in');
49
49
  field(runtime.output, 'credential', written.path);
50
50
  field(runtime.output, 'proxy', auth.proxyUrl);
51
- note(runtime.output, 'Keep auth.json private. It contains your live Sleeve token');
51
+ note(runtime.output, 'Keep auth.json and patched app configs private. They contain your live Sleeve token');
52
52
  line(runtime.output);
53
53
  const apps = await runtime.selectApps();
54
54
  const harnesses = await runtime.setupHarnesses(auth, apps);
@@ -8,7 +8,7 @@ export async function validateAuth(auth) {
8
8
  'content-type': 'application/json',
9
9
  'sleeve-token': auth.token,
10
10
  'sleeve-provider': 'anthropic',
11
- 'sleeve-harness': 'claude-code',
11
+ 'sleeve-harness': 'claude code',
12
12
  },
13
13
  body: '{}',
14
14
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sleev",
3
- "version": "0.0.10",
3
+ "version": "0.0.11",
4
4
  "description": "Sleev command-line tools.",
5
5
  "type": "module",
6
6
  "bin": {