sleev 0.0.12 → 0.0.13
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 +19 -39
- package/dist/auth/browser.d.ts +12 -0
- package/dist/auth/browser.js +33 -24
- package/dist/auth/browser.js.map +1 -0
- package/dist/auth/firebase-tokens.d.ts +39 -0
- package/dist/auth/firebase-tokens.js +173 -0
- package/dist/auth/firebase-tokens.js.map +1 -0
- package/dist/auth/firebase.d.ts +17 -0
- package/dist/auth/firebase.js +55 -0
- package/dist/auth/firebase.js.map +1 -0
- package/dist/auth/login.d.ts +22 -0
- package/dist/auth/login.js +31 -28
- package/dist/auth/login.js.map +1 -0
- package/dist/auth/loopback.d.ts +19 -0
- package/dist/auth/loopback.js +114 -44
- package/dist/auth/loopback.js.map +1 -0
- package/dist/auth/schema.d.ts +26 -0
- package/dist/auth/schema.js +59 -17
- package/dist/auth/schema.js.map +1 -0
- package/dist/auth/token.d.ts +19 -0
- package/dist/auth/token.js +53 -0
- package/dist/auth/token.js.map +1 -0
- package/dist/auth/verify.d.ts +6 -0
- package/dist/auth/verify.js +22 -0
- package/dist/auth/verify.js.map +1 -0
- package/dist/cli.d.ts +8 -0
- package/dist/cli.js +245 -59
- package/dist/cli.js.map +1 -0
- package/dist/config/paths.d.ts +12 -0
- package/dist/config/paths.js +34 -0
- package/dist/config/paths.js.map +1 -0
- package/dist/config/store.d.ts +63 -0
- package/dist/config/store.js +270 -0
- package/dist/config/store.js.map +1 -0
- package/dist/control/local-installations.d.ts +13 -0
- package/dist/control/local-installations.js +37 -0
- package/dist/control/local-installations.js.map +1 -0
- package/dist/daemon/config.d.ts +10 -0
- package/dist/daemon/config.js +52 -0
- package/dist/daemon/config.js.map +1 -0
- package/dist/daemon/controller.d.ts +29 -0
- package/dist/daemon/controller.js +34 -0
- package/dist/daemon/controller.js.map +1 -0
- package/dist/daemon/health.d.ts +8 -0
- package/dist/daemon/health.js +47 -0
- package/dist/daemon/health.js.map +1 -0
- package/dist/daemon/start.d.ts +5 -0
- package/dist/daemon/start.js +24 -0
- package/dist/daemon/start.js.map +1 -0
- package/dist/daemon/systemd.d.ts +35 -0
- package/dist/daemon/systemd.js +181 -0
- package/dist/daemon/systemd.js.map +1 -0
- package/dist/gateway/acquire.d.ts +14 -0
- package/dist/gateway/acquire.js +112 -0
- package/dist/gateway/acquire.js.map +1 -0
- package/dist/gateway/manifest.d.ts +26 -0
- package/dist/gateway/manifest.js +98 -0
- package/dist/gateway/manifest.js.map +1 -0
- package/dist/gateway/platform.d.ts +5 -0
- package/dist/gateway/platform.js +27 -0
- package/dist/gateway/platform.js.map +1 -0
- package/dist/harness/claude.d.ts +8 -0
- package/dist/{apps → harness}/claude.js +28 -28
- package/dist/harness/claude.js.map +1 -0
- package/dist/harness/codex.d.ts +10 -0
- package/dist/{apps → harness}/codex.js +50 -45
- package/dist/harness/codex.js.map +1 -0
- package/dist/harness/index.d.ts +13 -0
- package/dist/harness/index.js +63 -0
- package/dist/harness/index.js.map +1 -0
- package/dist/harness/opencode-config.d.ts +8 -0
- package/dist/{apps → harness}/opencode-config.js +116 -83
- package/dist/harness/opencode-config.js.map +1 -0
- package/dist/harness/opencode.d.ts +14 -0
- package/dist/harness/opencode.js +50 -0
- package/dist/harness/opencode.js.map +1 -0
- package/dist/harness/providers.d.ts +8 -0
- package/dist/harness/providers.js +30 -0
- package/dist/harness/providers.js.map +1 -0
- package/dist/harness/types.d.ts +14 -0
- package/dist/harness/types.js +2 -0
- package/dist/harness/types.js.map +1 -0
- package/dist/product.d.ts +12 -0
- package/dist/product.js +53 -10
- package/dist/product.js.map +1 -0
- package/dist/tui/clipboard.d.ts +1 -0
- package/dist/tui/clipboard.js +48 -0
- package/dist/tui/clipboard.js.map +1 -0
- package/dist/tui/openTuiApp.d.ts +1 -0
- package/dist/tui/openTuiApp.js +1436 -0
- package/dist/tui/openTuiApp.js.map +1 -0
- package/dist/tui/pages/flow-screen.d.ts +50 -0
- package/dist/tui/pages/flow-screen.js +435 -0
- package/dist/tui/pages/flow-screen.js.map +1 -0
- package/dist/tui/pages/footer.d.ts +7 -0
- package/dist/tui/pages/footer.js +14 -0
- package/dist/tui/pages/footer.js.map +1 -0
- package/dist/tui/pages/home-screen.d.ts +19 -0
- package/dist/tui/pages/home-screen.js +89 -0
- package/dist/tui/pages/home-screen.js.map +1 -0
- package/dist/tui/pages/index.d.ts +17 -0
- package/dist/tui/pages/index.js +11 -0
- package/dist/tui/pages/index.js.map +1 -0
- package/dist/tui/pages/local-setup-screen.d.ts +23 -0
- package/dist/tui/pages/local-setup-screen.js +134 -0
- package/dist/tui/pages/local-setup-screen.js.map +1 -0
- package/dist/tui/pages/location-card.d.ts +19 -0
- package/dist/tui/pages/location-card.js +116 -0
- package/dist/tui/pages/location-card.js.map +1 -0
- package/dist/tui/pages/menu-screen.d.ts +24 -0
- package/dist/tui/pages/menu-screen.js +117 -0
- package/dist/tui/pages/menu-screen.js.map +1 -0
- package/dist/tui/pages/session-grid.d.ts +23 -0
- package/dist/tui/pages/session-grid.js +552 -0
- package/dist/tui/pages/session-grid.js.map +1 -0
- package/dist/tui/pages/setup-screen.d.ts +15 -0
- package/dist/tui/pages/setup-screen.js +80 -0
- package/dist/tui/pages/setup-screen.js.map +1 -0
- package/dist/tui/pages/terminal-log.d.ts +14 -0
- package/dist/tui/pages/terminal-log.js +104 -0
- package/dist/tui/pages/terminal-log.js.map +1 -0
- package/dist/tui/pages/welcome-screen.d.ts +16 -0
- package/dist/tui/pages/welcome-screen.js +80 -0
- package/dist/tui/pages/welcome-screen.js.map +1 -0
- package/dist/tui/saved-tokens.d.ts +10 -0
- package/dist/tui/saved-tokens.js +104 -0
- package/dist/tui/saved-tokens.js.map +1 -0
- package/dist/tui/theme.d.ts +25 -0
- package/dist/tui/theme.js +27 -0
- package/dist/tui/theme.js.map +1 -0
- package/package.json +55 -38
- package/dist/apps/opencode.js +0 -158
- package/dist/apps.js +0 -148
- package/dist/auth/command.js +0 -109
- package/dist/auth/index.js +0 -2
- package/dist/auth/validation.js +0 -27
- package/dist/credentials/index.js +0 -3
- package/dist/credentials/schema.js +0 -63
- package/dist/credentials/store.js +0 -52
- package/dist/harnesses.js +0 -66
- package/dist/index.js +0 -3
- package/dist/providers.js +0 -30
- package/dist/terminal.js +0 -55
package/dist/apps/opencode.js
DELETED
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
// OpenCode setup owns credential discovery and provider selection for config patching.
|
|
2
|
-
import { readFile } from 'node:fs/promises';
|
|
3
|
-
import { emitKeypressEvents } from 'node:readline';
|
|
4
|
-
import { PROVIDERS } from '../providers.js';
|
|
5
|
-
import { field, line, note, prompt, success } from '../terminal.js';
|
|
6
|
-
import { openCodeAuthPath, openCodeJsoncPath, openCodePath, patchOpenCodeConfig, resolveOpenCodePath, unpatchOpenCodeConfig, } from './opencode-config.js';
|
|
7
|
-
export async function setupOpenCode(auth, input = process.stdin, output = process.stdout, options = {}) {
|
|
8
|
-
const path = options.configPath ?? (await resolveOpenCodePath());
|
|
9
|
-
const available = await availableOpenCodeProviders(options.authPath);
|
|
10
|
-
if (available.length === 0) {
|
|
11
|
-
note(output, 'Skipping OpenCode setup because no supported OpenCode credentials were found');
|
|
12
|
-
return null;
|
|
13
|
-
}
|
|
14
|
-
const providers = await selectOpenCodeProviders(available, input, output);
|
|
15
|
-
if (providers.length === 0)
|
|
16
|
-
return null;
|
|
17
|
-
const results = [];
|
|
18
|
-
for (const provider of providers) {
|
|
19
|
-
results.push(await patchOpenCode(auth, path, provider.id));
|
|
20
|
-
}
|
|
21
|
-
success(output, 'Configured OpenCode');
|
|
22
|
-
field(output, 'providers', providers.map((provider) => provider.label).join(', '));
|
|
23
|
-
field(output, 'config', path);
|
|
24
|
-
return results[0] ?? null;
|
|
25
|
-
}
|
|
26
|
-
export async function availableOpenCodeProviders(path = openCodeAuthPath()) {
|
|
27
|
-
const auth = await readAuth(path);
|
|
28
|
-
return openCodeProviders().filter((provider) => provider.id in auth);
|
|
29
|
-
}
|
|
30
|
-
export async function patchOpenCode(auth, path, provider = 'openai') {
|
|
31
|
-
const target = await patchOpenCodeConfig(auth, path, provider);
|
|
32
|
-
return { path: target, provider };
|
|
33
|
-
}
|
|
34
|
-
export async function unpatchOpenCode(path) {
|
|
35
|
-
await unpatchOpenCodeConfig(path);
|
|
36
|
-
}
|
|
37
|
-
export { openCodeAuthPath, openCodeJsoncPath, openCodePath, resolveOpenCodePath };
|
|
38
|
-
async function selectOpenCodeProviders(providers, input, output) {
|
|
39
|
-
if (providers.length === 1) {
|
|
40
|
-
const provider = providers[0];
|
|
41
|
-
if (!provider)
|
|
42
|
-
return [];
|
|
43
|
-
note(output, `Using OpenCode credential: ${provider.label}`);
|
|
44
|
-
return [provider];
|
|
45
|
-
}
|
|
46
|
-
if (!canSelect(input)) {
|
|
47
|
-
note(output, 'Skipping OpenCode setup because this terminal is not interactive');
|
|
48
|
-
return [];
|
|
49
|
-
}
|
|
50
|
-
return selectTty(providers, input, output);
|
|
51
|
-
}
|
|
52
|
-
function selectTty(providers, input, output) {
|
|
53
|
-
return new Promise((resolve, reject) => {
|
|
54
|
-
let cursor = 0;
|
|
55
|
-
let selected = new Set();
|
|
56
|
-
let rendered = false;
|
|
57
|
-
const render = () => {
|
|
58
|
-
if (rendered)
|
|
59
|
-
output.write(`\x1b[${providers.length + 2}A`);
|
|
60
|
-
output.write(`${prompt(output, 'choose the OpenCode credentials to patch')}\x1b[K\n`);
|
|
61
|
-
for (const [index, provider] of providers.entries()) {
|
|
62
|
-
const marker = index === cursor ? '>' : ' ';
|
|
63
|
-
const checked = selected.has(provider.id) ? 'x' : ' ';
|
|
64
|
-
output.write(`${marker} (${checked}) ${provider.label}\x1b[K\n`);
|
|
65
|
-
}
|
|
66
|
-
output.write(' space toggles, enter confirms\x1b[K\n');
|
|
67
|
-
rendered = true;
|
|
68
|
-
};
|
|
69
|
-
const cleanup = () => {
|
|
70
|
-
input.off('keypress', onKey);
|
|
71
|
-
input.setRawMode?.(false);
|
|
72
|
-
input.pause();
|
|
73
|
-
output.write('\x1b[?25h');
|
|
74
|
-
};
|
|
75
|
-
const done = () => {
|
|
76
|
-
cleanup();
|
|
77
|
-
line(output);
|
|
78
|
-
resolve(providers.filter((provider) => selected.has(provider.id)));
|
|
79
|
-
};
|
|
80
|
-
const cancel = () => {
|
|
81
|
-
cleanup();
|
|
82
|
-
line(output);
|
|
83
|
-
reject(new Error('Canceled.'));
|
|
84
|
-
};
|
|
85
|
-
const move = (delta) => {
|
|
86
|
-
cursor = (cursor + delta + providers.length) % providers.length;
|
|
87
|
-
render();
|
|
88
|
-
};
|
|
89
|
-
const toggle = () => {
|
|
90
|
-
const provider = providers[cursor];
|
|
91
|
-
if (!provider)
|
|
92
|
-
return;
|
|
93
|
-
const next = new Set(selected);
|
|
94
|
-
if (next.has(provider.id))
|
|
95
|
-
next.delete(provider.id);
|
|
96
|
-
else
|
|
97
|
-
next.add(provider.id);
|
|
98
|
-
selected = next;
|
|
99
|
-
render();
|
|
100
|
-
};
|
|
101
|
-
const onKey = (_chunk, key = {}) => {
|
|
102
|
-
if (key.ctrl && key.name === 'c') {
|
|
103
|
-
cancel();
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
if (key.name === 'up' || key.name === 'k') {
|
|
107
|
-
move(-1);
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
if (key.name === 'down' || key.name === 'j') {
|
|
111
|
-
move(1);
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
if (key.name === 'space' || key.sequence === ' ') {
|
|
115
|
-
toggle();
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
if (key.name === 'return' || key.name === 'enter')
|
|
119
|
-
done();
|
|
120
|
-
};
|
|
121
|
-
emitKeypressEvents(input);
|
|
122
|
-
input.setRawMode?.(true);
|
|
123
|
-
input.resume();
|
|
124
|
-
input.on('keypress', onKey);
|
|
125
|
-
output.write('\x1b[?25l');
|
|
126
|
-
render();
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
async function readAuth(path) {
|
|
130
|
-
try {
|
|
131
|
-
const raw = await readFile(path, 'utf8');
|
|
132
|
-
const parsed = JSON.parse(raw);
|
|
133
|
-
return object(parsed);
|
|
134
|
-
}
|
|
135
|
-
catch (err) {
|
|
136
|
-
if (code(err) === 'ENOENT')
|
|
137
|
-
return {};
|
|
138
|
-
throw err;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
function openCodeProviders() {
|
|
142
|
-
return PROVIDERS;
|
|
143
|
-
}
|
|
144
|
-
function object(value) {
|
|
145
|
-
if (typeof value !== 'object' || value === null || Array.isArray(value))
|
|
146
|
-
return {};
|
|
147
|
-
return value;
|
|
148
|
-
}
|
|
149
|
-
function canSelect(input) {
|
|
150
|
-
const tty = input;
|
|
151
|
-
return tty.isTTY === true && typeof tty.setRawMode === 'function';
|
|
152
|
-
}
|
|
153
|
-
function code(err) {
|
|
154
|
-
if (typeof err !== 'object' || err === null || !('code' in err))
|
|
155
|
-
return undefined;
|
|
156
|
-
const value = err.code;
|
|
157
|
-
return typeof value === 'string' ? value : undefined;
|
|
158
|
-
}
|
package/dist/apps.js
DELETED
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
// Interactive app selection for the post-login setup flow.
|
|
2
|
-
import { constants } from 'node:fs';
|
|
3
|
-
import { access } from 'node:fs/promises';
|
|
4
|
-
import { delimiter, join } from 'node:path';
|
|
5
|
-
import { emitKeypressEvents } from 'node:readline';
|
|
6
|
-
import { HARNESSES } from './harnesses.js';
|
|
7
|
-
import { line, note, prompt } from './terminal.js';
|
|
8
|
-
export const APPS = HARNESSES;
|
|
9
|
-
export async function selectApps(input = process.stdin, output = process.stdout, apps) {
|
|
10
|
-
const available = apps ?? (await detectApps());
|
|
11
|
-
if (available.length === 0) {
|
|
12
|
-
note(output, 'Skipping app setup because no supported local harnesses were detected');
|
|
13
|
-
return [];
|
|
14
|
-
}
|
|
15
|
-
if (canSelect(input, output))
|
|
16
|
-
return selectTty(available, input, output);
|
|
17
|
-
note(output, 'Skipping app setup because this terminal is not interactive');
|
|
18
|
-
return [];
|
|
19
|
-
}
|
|
20
|
-
export async function detectApps(env = process.env, platform = process.platform) {
|
|
21
|
-
const checks = await Promise.all(APPS.map(async (app) => ((await detected(app, env, platform)) ? app : null)));
|
|
22
|
-
return checks.filter((app) => app !== null);
|
|
23
|
-
}
|
|
24
|
-
export function appLabels(ids) {
|
|
25
|
-
if (ids.length === 0)
|
|
26
|
-
return 'none';
|
|
27
|
-
const labels = ids.map((id) => APPS.find((app) => app.id === id)?.label ?? id);
|
|
28
|
-
return labels.join(', ');
|
|
29
|
-
}
|
|
30
|
-
function selectTty(apps, input, output) {
|
|
31
|
-
return new Promise((resolve, reject) => {
|
|
32
|
-
const initial = new Set();
|
|
33
|
-
let cursor = 0;
|
|
34
|
-
let selected = initial;
|
|
35
|
-
let rendered = false;
|
|
36
|
-
const render = () => {
|
|
37
|
-
if (rendered)
|
|
38
|
-
output.write(`\x1b[${apps.length + 2}A`);
|
|
39
|
-
output.write(`${prompt(output, 'choose the apps you want to use Sleev with')}\x1b[K\n`);
|
|
40
|
-
for (const [index, app] of apps.entries()) {
|
|
41
|
-
const marker = index === cursor ? '>' : ' ';
|
|
42
|
-
const checked = selected.has(app.id) ? 'x' : ' ';
|
|
43
|
-
output.write(`${marker} (${checked}) ${app.label}\x1b[K\n`);
|
|
44
|
-
}
|
|
45
|
-
output.write(' space toggles, enter confirms\x1b[K\n');
|
|
46
|
-
rendered = true;
|
|
47
|
-
};
|
|
48
|
-
const cleanup = () => {
|
|
49
|
-
input.off('keypress', onKey);
|
|
50
|
-
input.setRawMode?.(false);
|
|
51
|
-
input.pause();
|
|
52
|
-
output.write('\x1b[?25h');
|
|
53
|
-
};
|
|
54
|
-
const done = () => {
|
|
55
|
-
cleanup();
|
|
56
|
-
line(output);
|
|
57
|
-
resolve(apps.filter((app) => selected.has(app.id)).map((app) => app.id));
|
|
58
|
-
};
|
|
59
|
-
const cancel = () => {
|
|
60
|
-
cleanup();
|
|
61
|
-
line(output);
|
|
62
|
-
reject(new Error('Canceled.'));
|
|
63
|
-
};
|
|
64
|
-
const move = (delta) => {
|
|
65
|
-
cursor = (cursor + delta + apps.length) % apps.length;
|
|
66
|
-
render();
|
|
67
|
-
};
|
|
68
|
-
const toggle = () => {
|
|
69
|
-
const app = apps[cursor];
|
|
70
|
-
if (!app)
|
|
71
|
-
return;
|
|
72
|
-
const next = new Set(selected);
|
|
73
|
-
if (next.has(app.id))
|
|
74
|
-
next.delete(app.id);
|
|
75
|
-
else
|
|
76
|
-
next.add(app.id);
|
|
77
|
-
selected = next;
|
|
78
|
-
render();
|
|
79
|
-
};
|
|
80
|
-
const toggleAll = () => {
|
|
81
|
-
selected = selected.size === apps.length ? new Set() : new Set(apps.map((app) => app.id));
|
|
82
|
-
render();
|
|
83
|
-
};
|
|
84
|
-
const onKey = (_chunk, key = {}) => {
|
|
85
|
-
if (key.ctrl && key.name === 'c') {
|
|
86
|
-
cancel();
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
if (key.name === 'up' || key.name === 'k') {
|
|
90
|
-
move(-1);
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
if (key.name === 'down' || key.name === 'j') {
|
|
94
|
-
move(1);
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
if (key.name === 'space' || key.sequence === ' ') {
|
|
98
|
-
toggle();
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
if (key.name === 'a' || key.sequence === 'a') {
|
|
102
|
-
toggleAll();
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
if (key.name === 'return' || key.name === 'enter')
|
|
106
|
-
done();
|
|
107
|
-
};
|
|
108
|
-
emitKeypressEvents(input);
|
|
109
|
-
input.setRawMode?.(true);
|
|
110
|
-
input.resume();
|
|
111
|
-
input.on('keypress', onKey);
|
|
112
|
-
output.write('\x1b[?25l');
|
|
113
|
-
render();
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
function canSelect(input, output) {
|
|
117
|
-
const tty = input;
|
|
118
|
-
return tty.isTTY === true && typeof tty.setRawMode === 'function';
|
|
119
|
-
}
|
|
120
|
-
async function exists(command, env, platform) {
|
|
121
|
-
const path = env.PATH ?? '';
|
|
122
|
-
const dirs = path.split(delimiter).filter(Boolean);
|
|
123
|
-
const names = platform === 'win32' ? windowsNames(command, env) : [command];
|
|
124
|
-
const paths = dirs.flatMap((dir) => names.map((name) => join(dir, name)));
|
|
125
|
-
const checks = await Promise.all(paths.map((path) => executable(path)));
|
|
126
|
-
return checks.some(Boolean);
|
|
127
|
-
}
|
|
128
|
-
async function detected(app, env, platform) {
|
|
129
|
-
if (!(await exists(app.command, env, platform)))
|
|
130
|
-
return false;
|
|
131
|
-
if (!app.available)
|
|
132
|
-
return true;
|
|
133
|
-
return app.available();
|
|
134
|
-
}
|
|
135
|
-
function windowsNames(command, env) {
|
|
136
|
-
const ext = env.PATHEXT ?? '.COM;.EXE;.BAT;.CMD';
|
|
137
|
-
const exts = ext.split(';').filter(Boolean);
|
|
138
|
-
return [command, ...exts.map((item) => `${command}${item.toLowerCase()}`), ...exts.map((item) => `${command}${item.toUpperCase()}`)];
|
|
139
|
-
}
|
|
140
|
-
async function executable(path) {
|
|
141
|
-
try {
|
|
142
|
-
await access(path, constants.X_OK);
|
|
143
|
-
return true;
|
|
144
|
-
}
|
|
145
|
-
catch {
|
|
146
|
-
return false;
|
|
147
|
-
}
|
|
148
|
-
}
|
package/dist/auth/command.js
DELETED
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
// Auth command flows for the CLI dispatcher.
|
|
2
|
-
import { selectApps } from '../apps.js';
|
|
3
|
-
import { authPath, clearAuth, createAuthFile, readAuth, writeAuth } from '../credentials/index.js';
|
|
4
|
-
import { setupHarnesses, unpatchHarnesses } from '../harnesses.js';
|
|
5
|
-
import { proxyUrl } from '../product.js';
|
|
6
|
-
import { field, line, note, success } from '../terminal.js';
|
|
7
|
-
import { login as browserLogin } from './login.js';
|
|
8
|
-
import { validateAuth } from './validation.js';
|
|
9
|
-
export function commandRuntime() {
|
|
10
|
-
return {
|
|
11
|
-
output: process.stdout,
|
|
12
|
-
authPath,
|
|
13
|
-
readAuth,
|
|
14
|
-
writeAuth,
|
|
15
|
-
clearAuth,
|
|
16
|
-
createAuthFile,
|
|
17
|
-
login: browserLogin,
|
|
18
|
-
validateAuth,
|
|
19
|
-
selectApps: () => selectApps(),
|
|
20
|
-
setupHarnesses,
|
|
21
|
-
unpatchHarnesses,
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
export async function loginCommand(options = {}, runtime = commandRuntime()) {
|
|
25
|
-
const path = runtime.authPath();
|
|
26
|
-
const existing = await readLocalAuth(path, runtime);
|
|
27
|
-
const local = existing ? canonicalAuth(existing) : null;
|
|
28
|
-
const state = local && !options.force ? await runtime.validateAuth(local) : 'invalid';
|
|
29
|
-
if (state === 'valid') {
|
|
30
|
-
note(runtime.output, 'Using existing valid local auth');
|
|
31
|
-
}
|
|
32
|
-
if (existing && options.force) {
|
|
33
|
-
note(runtime.output, 'Replacing existing local auth. The previous remote Sleeve token is not revoked');
|
|
34
|
-
}
|
|
35
|
-
if (existing && state === 'invalid' && !options.force) {
|
|
36
|
-
note(runtime.output, 'Existing local auth is expired or revoked. Creating a new token');
|
|
37
|
-
}
|
|
38
|
-
if (existing && state === 'unknown' && !options.force) {
|
|
39
|
-
note(runtime.output, 'Could not verify existing local auth. Creating a new token');
|
|
40
|
-
}
|
|
41
|
-
const useLocal = state === 'valid' && local;
|
|
42
|
-
const auth = useLocal ? local : runtime.createAuthFile(await runtime.login());
|
|
43
|
-
const changed = useLocal && existing?.proxyUrl !== auth.proxyUrl;
|
|
44
|
-
const written = useLocal && !changed ? { path } : await runtime.writeAuth(auth, path);
|
|
45
|
-
if (changed)
|
|
46
|
-
note(runtime.output, 'Updated local auth to use the current Sleev proxy');
|
|
47
|
-
line(runtime.output);
|
|
48
|
-
success(runtime.output, useLocal ? 'Already logged in' : 'Logged in');
|
|
49
|
-
field(runtime.output, 'credential', written.path);
|
|
50
|
-
field(runtime.output, 'proxy', auth.proxyUrl);
|
|
51
|
-
note(runtime.output, 'Keep auth.json and patched app configs private. They contain your live Sleeve token');
|
|
52
|
-
line(runtime.output);
|
|
53
|
-
const apps = await runtime.selectApps();
|
|
54
|
-
const harnesses = await runtime.setupHarnesses(auth, apps);
|
|
55
|
-
success(runtime.output, 'Setup complete');
|
|
56
|
-
field(runtime.output, 'applications configured to use sleev', harnesses.label);
|
|
57
|
-
return 0;
|
|
58
|
-
}
|
|
59
|
-
async function readLocalAuth(path, runtime) {
|
|
60
|
-
try {
|
|
61
|
-
return await runtime.readAuth(path);
|
|
62
|
-
}
|
|
63
|
-
catch (err) {
|
|
64
|
-
if (!recoverableAuthError(err))
|
|
65
|
-
throw err;
|
|
66
|
-
note(runtime.output, 'Replacing invalid local auth file');
|
|
67
|
-
return null;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
export async function logoutCommand(runtime = commandRuntime()) {
|
|
71
|
-
const path = runtime.authPath();
|
|
72
|
-
const existing = await readLogoutAuth(path, runtime);
|
|
73
|
-
const cleanup = await runtime.unpatchHarnesses();
|
|
74
|
-
await runtime.clearAuth(path);
|
|
75
|
-
if (!existing) {
|
|
76
|
-
note(runtime.output, 'No local auth file found');
|
|
77
|
-
return 0;
|
|
78
|
-
}
|
|
79
|
-
success(runtime.output, 'Removed local auth');
|
|
80
|
-
field(runtime.output, 'removed', path);
|
|
81
|
-
note(runtime.output, 'Remote Sleeve token is not revoked and remains active');
|
|
82
|
-
if (cleanup.failures[0])
|
|
83
|
-
note(runtime.output, `Some harness config cleanup failed: ${cleanup.failures.join('; ')}`);
|
|
84
|
-
return 0;
|
|
85
|
-
}
|
|
86
|
-
function recoverableAuthError(err) {
|
|
87
|
-
if (err instanceof SyntaxError)
|
|
88
|
-
return true;
|
|
89
|
-
if (!(err instanceof Error))
|
|
90
|
-
return false;
|
|
91
|
-
return err.message.startsWith('Sleev auth ');
|
|
92
|
-
}
|
|
93
|
-
function canonicalAuth(auth) {
|
|
94
|
-
const proxy = proxyUrl();
|
|
95
|
-
if (auth.proxyUrl === proxy)
|
|
96
|
-
return auth;
|
|
97
|
-
return { ...auth, proxyUrl: proxy };
|
|
98
|
-
}
|
|
99
|
-
async function readLogoutAuth(path, runtime) {
|
|
100
|
-
try {
|
|
101
|
-
return await runtime.readAuth(path);
|
|
102
|
-
}
|
|
103
|
-
catch (err) {
|
|
104
|
-
if (!recoverableAuthError(err))
|
|
105
|
-
throw err;
|
|
106
|
-
note(runtime.output, 'Removing invalid local auth file');
|
|
107
|
-
return { version: 1, token: '', label: null, proxyUrl: '', createdAt: '' };
|
|
108
|
-
}
|
|
109
|
-
}
|
package/dist/auth/index.js
DELETED
package/dist/auth/validation.js
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
// Auth validation owns Sleeve token checks against the proxy.
|
|
2
|
-
export async function validateAuth(auth) {
|
|
3
|
-
const base = trim(auth.proxyUrl);
|
|
4
|
-
try {
|
|
5
|
-
const res = await fetch(`${base}/messages`, {
|
|
6
|
-
method: 'POST',
|
|
7
|
-
headers: {
|
|
8
|
-
'content-type': 'application/json',
|
|
9
|
-
'sleeve-token': auth.token,
|
|
10
|
-
'sleeve-provider': 'anthropic',
|
|
11
|
-
'sleeve-harness': 'claude code',
|
|
12
|
-
},
|
|
13
|
-
body: '{}',
|
|
14
|
-
});
|
|
15
|
-
if (res.status === 401)
|
|
16
|
-
return 'invalid';
|
|
17
|
-
if (res.status === 400)
|
|
18
|
-
return 'valid';
|
|
19
|
-
}
|
|
20
|
-
catch {
|
|
21
|
-
return 'unknown';
|
|
22
|
-
}
|
|
23
|
-
return 'unknown';
|
|
24
|
-
}
|
|
25
|
-
function trim(url) {
|
|
26
|
-
return url.replace(/\/+$/, '');
|
|
27
|
-
}
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
// Validation for the on-disk Sleev auth file.
|
|
2
|
-
export function createAuthFile(input) {
|
|
3
|
-
const parsed = parseAuthInput(input);
|
|
4
|
-
return { version: 1, ...parsed };
|
|
5
|
-
}
|
|
6
|
-
export function parseAuthInput(input) {
|
|
7
|
-
if (!isRecord(input))
|
|
8
|
-
throw new Error('Sleev auth input must be an object.');
|
|
9
|
-
return parseAuthRecord(input);
|
|
10
|
-
}
|
|
11
|
-
export function parseAuthFile(input) {
|
|
12
|
-
if (!isRecord(input))
|
|
13
|
-
throw new Error('Sleev auth file must be an object.');
|
|
14
|
-
const version = input.version;
|
|
15
|
-
if (version !== 1)
|
|
16
|
-
throw new Error('Sleev auth file version is unsupported.');
|
|
17
|
-
return { version, ...parseAuthInput(input) };
|
|
18
|
-
}
|
|
19
|
-
function parseAuthRecord(input) {
|
|
20
|
-
if (!isRecord(input))
|
|
21
|
-
throw new Error('Sleev auth input must be an object.');
|
|
22
|
-
const token = stringField(input, 'token');
|
|
23
|
-
const label = nullableStringField(input, 'label');
|
|
24
|
-
const proxyUrl = stringField(input, 'proxyUrl');
|
|
25
|
-
const createdAt = stringField(input, 'createdAt');
|
|
26
|
-
if (!token.startsWith('slv_live_'))
|
|
27
|
-
throw new Error('Sleev auth token is invalid.');
|
|
28
|
-
validateUrl(proxyUrl, 'Sleev auth proxy URL');
|
|
29
|
-
if (Number.isNaN(Date.parse(createdAt)))
|
|
30
|
-
throw new Error('Sleev auth createdAt is invalid.');
|
|
31
|
-
return { token, label, proxyUrl, createdAt };
|
|
32
|
-
}
|
|
33
|
-
function stringField(input, key) {
|
|
34
|
-
const value = input[key];
|
|
35
|
-
if (typeof value !== 'string')
|
|
36
|
-
throw new Error(`${key} must be a string.`);
|
|
37
|
-
return value;
|
|
38
|
-
}
|
|
39
|
-
function nullableStringField(input, key) {
|
|
40
|
-
const value = input[key];
|
|
41
|
-
if (value === null || typeof value === 'undefined')
|
|
42
|
-
return null;
|
|
43
|
-
if (typeof value !== 'string')
|
|
44
|
-
throw new Error(`${key} must be a string or null.`);
|
|
45
|
-
return value;
|
|
46
|
-
}
|
|
47
|
-
function validateUrl(raw, name) {
|
|
48
|
-
const url = parseUrl(raw, name);
|
|
49
|
-
if (url.protocol !== 'https:' && url.protocol !== 'http:') {
|
|
50
|
-
throw new Error(`${name} must use http or https.`);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
function parseUrl(raw, name) {
|
|
54
|
-
try {
|
|
55
|
-
return new URL(raw);
|
|
56
|
-
}
|
|
57
|
-
catch {
|
|
58
|
-
throw new Error(`${name} must be a valid URL.`);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
function isRecord(input) {
|
|
62
|
-
return typeof input === 'object' && input !== null && !Array.isArray(input);
|
|
63
|
-
}
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
// Credential storage owns auth.json path resolution, validation, and safe writes.
|
|
2
|
-
import { chmod, mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises';
|
|
3
|
-
import { dirname, join } from 'node:path';
|
|
4
|
-
import { homedir } from 'node:os';
|
|
5
|
-
import { parseAuthFile } from './schema.js';
|
|
6
|
-
export function authPath(env = process.env, platform = process.platform) {
|
|
7
|
-
return join(configRoot(env, platform), 'sleev', 'auth.json');
|
|
8
|
-
}
|
|
9
|
-
export async function readAuth(path = authPath()) {
|
|
10
|
-
try {
|
|
11
|
-
const raw = await readFile(path, 'utf8');
|
|
12
|
-
return parseAuthFile(JSON.parse(raw));
|
|
13
|
-
}
|
|
14
|
-
catch (err) {
|
|
15
|
-
if (errorCode(err) === 'ENOENT')
|
|
16
|
-
return null;
|
|
17
|
-
throw err;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
export async function writeAuth(auth, path = authPath()) {
|
|
21
|
-
const checked = parseAuthFile(auth);
|
|
22
|
-
const dir = dirname(path);
|
|
23
|
-
const tmp = join(dir, `.auth.${process.pid}.${Date.now()}.tmp`);
|
|
24
|
-
const body = `${JSON.stringify(checked, null, 2)}\n`;
|
|
25
|
-
await mkdir(dir, { recursive: true, mode: 0o700 });
|
|
26
|
-
await writeFile(tmp, body, { mode: 0o600 });
|
|
27
|
-
await rename(tmp, path);
|
|
28
|
-
await chmod(path, 0o600);
|
|
29
|
-
return { path };
|
|
30
|
-
}
|
|
31
|
-
export async function clearAuth(path = authPath()) {
|
|
32
|
-
try {
|
|
33
|
-
await rm(path);
|
|
34
|
-
}
|
|
35
|
-
catch (err) {
|
|
36
|
-
if (errorCode(err) === 'ENOENT')
|
|
37
|
-
return;
|
|
38
|
-
throw err;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
function configRoot(env, platform) {
|
|
42
|
-
if (platform === 'win32') {
|
|
43
|
-
return env.APPDATA || join(homedir(), 'AppData', 'Roaming');
|
|
44
|
-
}
|
|
45
|
-
return env.XDG_CONFIG_HOME || join(homedir(), '.config');
|
|
46
|
-
}
|
|
47
|
-
function errorCode(err) {
|
|
48
|
-
if (typeof err !== 'object' || err === null || !('code' in err))
|
|
49
|
-
return undefined;
|
|
50
|
-
const code = err.code;
|
|
51
|
-
return typeof code === 'string' ? code : undefined;
|
|
52
|
-
}
|
package/dist/harnesses.js
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
// Harness orchestration owns setup and cleanup across supported local tools.
|
|
2
|
-
import { setupClaude, unpatchClaude } from './apps/claude.js';
|
|
3
|
-
import { setupCodex, unpatchCodex } from './apps/codex.js';
|
|
4
|
-
import { availableOpenCodeProviders, setupOpenCode, unpatchOpenCode } from './apps/opencode.js';
|
|
5
|
-
export const HARNESSES = [
|
|
6
|
-
{
|
|
7
|
-
id: 'opencode',
|
|
8
|
-
label: 'opencode',
|
|
9
|
-
command: 'opencode',
|
|
10
|
-
available: async () => {
|
|
11
|
-
try {
|
|
12
|
-
return (await availableOpenCodeProviders()).length > 0;
|
|
13
|
-
}
|
|
14
|
-
catch {
|
|
15
|
-
return false;
|
|
16
|
-
}
|
|
17
|
-
},
|
|
18
|
-
setup: setupOpenCode,
|
|
19
|
-
unpatch: unpatchOpenCode,
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
id: 'claude-code',
|
|
23
|
-
label: 'claude code',
|
|
24
|
-
command: 'claude',
|
|
25
|
-
setup: setupClaude,
|
|
26
|
-
unpatch: unpatchClaude,
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
id: 'codex',
|
|
30
|
-
label: 'codex',
|
|
31
|
-
command: 'codex',
|
|
32
|
-
setup: setupCodex,
|
|
33
|
-
unpatch: unpatchCodex,
|
|
34
|
-
},
|
|
35
|
-
];
|
|
36
|
-
export async function setupHarnesses(auth, apps) {
|
|
37
|
-
for (const harness of HARNESSES) {
|
|
38
|
-
if (!apps.includes(harness.id))
|
|
39
|
-
continue;
|
|
40
|
-
await harness.setup(auth);
|
|
41
|
-
}
|
|
42
|
-
return { label: harnessLabels(apps) };
|
|
43
|
-
}
|
|
44
|
-
export async function unpatchHarnesses() {
|
|
45
|
-
const failures = [];
|
|
46
|
-
await Promise.all(HARNESSES.map(async (harness) => {
|
|
47
|
-
try {
|
|
48
|
-
await harness.unpatch();
|
|
49
|
-
}
|
|
50
|
-
catch (err) {
|
|
51
|
-
failures.push(`${harness.label}: ${message(err)}`);
|
|
52
|
-
}
|
|
53
|
-
}));
|
|
54
|
-
return { failures };
|
|
55
|
-
}
|
|
56
|
-
export function harnessLabels(ids) {
|
|
57
|
-
if (ids.length === 0)
|
|
58
|
-
return 'none';
|
|
59
|
-
const labels = ids.map((id) => HARNESSES.find((harness) => harness.id === id)?.label ?? id);
|
|
60
|
-
return labels.join(', ');
|
|
61
|
-
}
|
|
62
|
-
function message(err) {
|
|
63
|
-
if (err instanceof Error)
|
|
64
|
-
return err.message;
|
|
65
|
-
return String(err);
|
|
66
|
-
}
|
package/dist/index.js
DELETED
package/dist/providers.js
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
// Provider registry owns Sleev support metadata shared across harness adapters.
|
|
2
|
-
export const PROVIDERS = [
|
|
3
|
-
{ id: 'openai', label: 'OpenAI' },
|
|
4
|
-
{ id: 'anthropic', label: 'Anthropic' },
|
|
5
|
-
{
|
|
6
|
-
id: 'opencode',
|
|
7
|
-
label: 'OpenCode Zen',
|
|
8
|
-
models: [
|
|
9
|
-
'claude-sonnet-4-5',
|
|
10
|
-
'claude-sonnet-4-6',
|
|
11
|
-
'claude-opus-4-5',
|
|
12
|
-
'claude-opus-4-6',
|
|
13
|
-
'claude-opus-4-7',
|
|
14
|
-
'gpt-5.3-codex',
|
|
15
|
-
'gpt-5.3-codex-spark',
|
|
16
|
-
'gpt-5.4',
|
|
17
|
-
'gpt-5.4-mini',
|
|
18
|
-
'gpt-5.4-nano',
|
|
19
|
-
'gpt-5.4-pro',
|
|
20
|
-
'gpt-5.5',
|
|
21
|
-
'gpt-5.5-pro',
|
|
22
|
-
],
|
|
23
|
-
},
|
|
24
|
-
];
|
|
25
|
-
export function provider(id) {
|
|
26
|
-
const value = PROVIDERS.find((item) => item.id === id);
|
|
27
|
-
if (!value)
|
|
28
|
-
throw new Error(`Unsupported provider: ${id}`);
|
|
29
|
-
return value;
|
|
30
|
-
}
|