sleev 0.0.0 → 0.0.10
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 +47 -1
- package/dist/apps/claude.js +90 -0
- package/dist/apps/codex.js +184 -0
- package/dist/apps/opencode-config.js +268 -0
- package/dist/apps/opencode.js +158 -0
- package/dist/apps.js +148 -0
- package/dist/auth/browser.js +80 -0
- package/dist/auth/command.js +109 -0
- package/dist/auth/index.js +2 -0
- package/dist/auth/login.js +55 -0
- package/dist/auth/loopback.js +134 -0
- package/dist/auth/schema.js +46 -0
- package/dist/auth/validation.js +27 -0
- package/dist/cli.js +69 -0
- package/dist/credentials/index.js +3 -0
- package/dist/credentials/schema.js +63 -0
- package/dist/credentials/store.js +52 -0
- package/dist/harnesses.js +66 -0
- package/dist/index.js +3 -0
- package/dist/product.js +27 -0
- package/dist/providers.js +30 -0
- package/dist/terminal.js +55 -0
- package/package.json +36 -6
- package/index.js +0 -1
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// Validation for the browser-to-CLI auth handoff payload.
|
|
2
|
+
export function parseHandoff(input) {
|
|
3
|
+
if (!isRecord(input))
|
|
4
|
+
throw new Error('Login callback payload must be an object.');
|
|
5
|
+
const state = stringField(input, 'state');
|
|
6
|
+
const token = stringField(input, 'token');
|
|
7
|
+
const label = nullableStringField(input, 'label');
|
|
8
|
+
const proxyUrl = stringField(input, 'proxyUrl');
|
|
9
|
+
if (!state)
|
|
10
|
+
throw new Error('Login callback state is missing.');
|
|
11
|
+
if (!token.startsWith('slv_live_'))
|
|
12
|
+
throw new Error('Login callback token is invalid.');
|
|
13
|
+
validateUrl(proxyUrl, 'Login callback proxy URL');
|
|
14
|
+
return { state, token, label, proxyUrl };
|
|
15
|
+
}
|
|
16
|
+
function stringField(input, key) {
|
|
17
|
+
const value = input[key];
|
|
18
|
+
if (typeof value !== 'string')
|
|
19
|
+
throw new Error(`${key} must be a string.`);
|
|
20
|
+
return value;
|
|
21
|
+
}
|
|
22
|
+
function nullableStringField(input, key) {
|
|
23
|
+
const value = input[key];
|
|
24
|
+
if (value === null || typeof value === 'undefined')
|
|
25
|
+
return null;
|
|
26
|
+
if (typeof value !== 'string')
|
|
27
|
+
throw new Error(`${key} must be a string or null.`);
|
|
28
|
+
return value;
|
|
29
|
+
}
|
|
30
|
+
function validateUrl(raw, name) {
|
|
31
|
+
const url = parseUrl(raw, name);
|
|
32
|
+
if (url.protocol !== 'https:' && url.protocol !== 'http:') {
|
|
33
|
+
throw new Error(`${name} must use http or https.`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function parseUrl(raw, name) {
|
|
37
|
+
try {
|
|
38
|
+
return new URL(raw);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
throw new Error(`${name} must be a valid URL.`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function isRecord(input) {
|
|
45
|
+
return typeof input === 'object' && input !== null && !Array.isArray(input);
|
|
46
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
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
|
+
}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Thin command dispatcher for the `sleev` executable.
|
|
3
|
+
import { existsSync, realpathSync } from 'node:fs';
|
|
4
|
+
import { pathToFileURL } from 'node:url';
|
|
5
|
+
import { loginCommand, logoutCommand } from './auth/command.js';
|
|
6
|
+
import { failure } from './terminal.js';
|
|
7
|
+
const HELP = `sleev\n\nUsage:\n sleev login [--force]\n sleev install [--force]\n sleev auth login [--force]\n sleev auth <command>\n\nCommands:\n login Open the Sleev web app when needed and store a local auth token\n install Alias for login\n auth login Open the Sleev web app when needed and store a local auth token\n auth logout Delete the local auth file\n`;
|
|
8
|
+
const LOGIN_HELP = `Usage:\n sleev login [--force]\n sleev install [--force]\n sleev auth login [--force]\n`;
|
|
9
|
+
const AUTH_HELP = `Usage:\n sleev auth login [--force]\n sleev auth logout\n`;
|
|
10
|
+
export async function main(argv = process.argv.slice(2)) {
|
|
11
|
+
if (argv.length === 0 || argv[0] === '--help' || argv[0] === '-h') {
|
|
12
|
+
process.stdout.write(HELP);
|
|
13
|
+
return 0;
|
|
14
|
+
}
|
|
15
|
+
if (argv[0] === 'login' || argv[0] === 'install') {
|
|
16
|
+
if (argv[1] === '--help' || argv[1] === '-h') {
|
|
17
|
+
process.stdout.write(LOGIN_HELP);
|
|
18
|
+
return 0;
|
|
19
|
+
}
|
|
20
|
+
return login(argv[0], argv.slice(1), LOGIN_HELP);
|
|
21
|
+
}
|
|
22
|
+
if (argv[0] !== 'auth') {
|
|
23
|
+
process.stderr.write(`Unknown command: ${argv[0]}\n\n${HELP}`);
|
|
24
|
+
return 1;
|
|
25
|
+
}
|
|
26
|
+
if (argv[1] === '--help' || argv[1] === '-h') {
|
|
27
|
+
process.stdout.write(AUTH_HELP);
|
|
28
|
+
return 0;
|
|
29
|
+
}
|
|
30
|
+
const command = argv[1];
|
|
31
|
+
if (command !== 'login' && command !== 'logout') {
|
|
32
|
+
process.stderr.write(`Unknown auth command: ${command ?? ''}\n\n${AUTH_HELP}`);
|
|
33
|
+
return 1;
|
|
34
|
+
}
|
|
35
|
+
if (command === 'logout') {
|
|
36
|
+
if (argv.length !== 2)
|
|
37
|
+
return usageError(`auth ${command} does not accept flags yet.`, AUTH_HELP);
|
|
38
|
+
return logoutCommand();
|
|
39
|
+
}
|
|
40
|
+
return login(`auth ${command}`, argv.slice(2), AUTH_HELP);
|
|
41
|
+
}
|
|
42
|
+
function login(command, flags, help) {
|
|
43
|
+
if (flags.some((flag) => flag !== '--force'))
|
|
44
|
+
return usageError(`Unknown ${command} flag.`, help);
|
|
45
|
+
return loginCommand({ force: flags.includes('--force') });
|
|
46
|
+
}
|
|
47
|
+
function usageError(message, help) {
|
|
48
|
+
process.stderr.write(`${message}\n\n${help}`);
|
|
49
|
+
return 1;
|
|
50
|
+
}
|
|
51
|
+
if (isEntry()) {
|
|
52
|
+
main().then((code) => {
|
|
53
|
+
process.exitCode = code;
|
|
54
|
+
}, (err) => {
|
|
55
|
+
const message = err instanceof Error ? err.message : 'Command failed.';
|
|
56
|
+
failure(process.stderr, message);
|
|
57
|
+
process.exitCode = 1;
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
function isEntry() {
|
|
61
|
+
const entry = process.argv[1];
|
|
62
|
+
if (!entry)
|
|
63
|
+
return false;
|
|
64
|
+
if (import.meta.url === pathToFileURL(entry).href)
|
|
65
|
+
return true;
|
|
66
|
+
if (!existsSync(entry))
|
|
67
|
+
return false;
|
|
68
|
+
return realpathSync(new URL(import.meta.url)) === realpathSync(entry);
|
|
69
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
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
ADDED
package/dist/product.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Product defaults and environment escape hatches for local development.
|
|
2
|
+
const DEFAULT_WEB_ORIGIN = 'https://sleev.ai';
|
|
3
|
+
const DEFAULT_PROXY_URL = 'https://api.sleev.ai';
|
|
4
|
+
export function webOrigin(env = process.env) {
|
|
5
|
+
const raw = env.SLEEV_WEB_ORIGIN?.trim();
|
|
6
|
+
if (!raw)
|
|
7
|
+
return DEFAULT_WEB_ORIGIN;
|
|
8
|
+
const url = parseUrl(raw, 'SLEEV_WEB_ORIGIN');
|
|
9
|
+
if (url.protocol !== 'https:' && url.protocol !== 'http:') {
|
|
10
|
+
throw new Error('SLEEV_WEB_ORIGIN must use http or https.');
|
|
11
|
+
}
|
|
12
|
+
if (url.pathname !== '/' || url.search || url.hash) {
|
|
13
|
+
throw new Error('SLEEV_WEB_ORIGIN must be an origin without a path.');
|
|
14
|
+
}
|
|
15
|
+
return url.origin;
|
|
16
|
+
}
|
|
17
|
+
export function proxyUrl() {
|
|
18
|
+
return DEFAULT_PROXY_URL;
|
|
19
|
+
}
|
|
20
|
+
function parseUrl(raw, name) {
|
|
21
|
+
try {
|
|
22
|
+
return new URL(raw);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
throw new Error(`${name} must be a valid URL.`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
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
|
+
}
|
package/dist/terminal.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// Terminal presentation helpers keep command output consistent without deps.
|
|
2
|
+
const styles = {
|
|
3
|
+
accent: ['\x1b[36m', '\x1b[39m'],
|
|
4
|
+
bold: ['\x1b[1m', '\x1b[22m'],
|
|
5
|
+
dim: ['\x1b[2m', '\x1b[22m'],
|
|
6
|
+
good: ['\x1b[32m', '\x1b[39m'],
|
|
7
|
+
bad: ['\x1b[31m', '\x1b[39m'],
|
|
8
|
+
};
|
|
9
|
+
export function brand(output) {
|
|
10
|
+
line(output, paint(output, 'accent', 'sleev'));
|
|
11
|
+
line(output);
|
|
12
|
+
}
|
|
13
|
+
export function step(output, text) {
|
|
14
|
+
line(output, `${paint(output, 'accent', '>')} ${text}`);
|
|
15
|
+
}
|
|
16
|
+
export function success(output, text) {
|
|
17
|
+
line(output, `${paint(output, 'good', 'OK')} ${text}`);
|
|
18
|
+
}
|
|
19
|
+
export function failure(output, text) {
|
|
20
|
+
line(output, `${paint(output, 'bad', 'ERR')} ${text}`);
|
|
21
|
+
}
|
|
22
|
+
export function note(output, text) {
|
|
23
|
+
line(output, paint(output, 'dim', text));
|
|
24
|
+
}
|
|
25
|
+
export function field(output, name, value) {
|
|
26
|
+
line(output, ` ${paint(output, 'dim', name.padEnd(10))} ${value}`);
|
|
27
|
+
}
|
|
28
|
+
export function choice(output, label, active) {
|
|
29
|
+
if (!active)
|
|
30
|
+
return ` ${label}`;
|
|
31
|
+
return `${paint(output, 'accent', '>')} ${paint(output, 'bold', label)}`;
|
|
32
|
+
}
|
|
33
|
+
export function prompt(output, text) {
|
|
34
|
+
return `${paint(output, 'accent', '?')} ${text} `;
|
|
35
|
+
}
|
|
36
|
+
export function inlineCode(output, value) {
|
|
37
|
+
return paint(output, 'bold', value);
|
|
38
|
+
}
|
|
39
|
+
export function line(output, text = '') {
|
|
40
|
+
output.write(`${text}\n`);
|
|
41
|
+
}
|
|
42
|
+
function paint(output, style, text) {
|
|
43
|
+
if (!color(output))
|
|
44
|
+
return text;
|
|
45
|
+
const [open, close] = styles[style];
|
|
46
|
+
return `${open}${text}${close}`;
|
|
47
|
+
}
|
|
48
|
+
function color(output) {
|
|
49
|
+
const stream = output;
|
|
50
|
+
if (process.env.NO_COLOR)
|
|
51
|
+
return false;
|
|
52
|
+
if (process.env.FORCE_COLOR && process.env.FORCE_COLOR !== '0')
|
|
53
|
+
return true;
|
|
54
|
+
return stream.isTTY === true;
|
|
55
|
+
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,40 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sleev",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "
|
|
5
|
-
"
|
|
6
|
-
"
|
|
3
|
+
"version": "0.0.10",
|
|
4
|
+
"description": "Sleev command-line tools.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"sleev": "dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"exports": {
|
|
10
|
+
".": "./dist/index.js"
|
|
11
|
+
},
|
|
7
12
|
"files": [
|
|
8
|
-
"
|
|
9
|
-
|
|
13
|
+
"dist",
|
|
14
|
+
"README.md"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
18
|
+
"build": "npm run clean && tsc -p tsconfig.json",
|
|
19
|
+
"dev": "npm run build && node dist/cli.js",
|
|
20
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
21
|
+
"test": "npm run build && node --test test/*.test.mjs",
|
|
22
|
+
"prepack": "npm run build"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"sleev",
|
|
26
|
+
"cli",
|
|
27
|
+
"auth"
|
|
28
|
+
],
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=20.0.0"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/node": "^24.12.2",
|
|
35
|
+
"typescript": "~6.0.2"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"jsonc-parser": "^3.3.1"
|
|
39
|
+
}
|
|
10
40
|
}
|
package/index.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";
|