opencodespaces 0.1.0
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 +104 -0
- package/dist/commands/login.d.ts +7 -0
- package/dist/commands/login.js +42 -0
- package/dist/commands/logout.d.ts +7 -0
- package/dist/commands/logout.js +20 -0
- package/dist/commands/sessions.d.ts +7 -0
- package/dist/commands/sessions.js +76 -0
- package/dist/commands/ssh.d.ts +12 -0
- package/dist/commands/ssh.js +141 -0
- package/dist/commands/sync.d.ts +14 -0
- package/dist/commands/sync.js +353 -0
- package/dist/commands/whoami.d.ts +7 -0
- package/dist/commands/whoami.js +50 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +38 -0
- package/dist/lib/api.d.ts +96 -0
- package/dist/lib/api.js +130 -0
- package/dist/lib/auth.d.ts +34 -0
- package/dist/lib/auth.js +174 -0
- package/dist/lib/config.d.ts +74 -0
- package/dist/lib/config.js +184 -0
- package/dist/lib/version.d.ts +4 -0
- package/dist/lib/version.js +4 -0
- package/dist/utils/logger.d.ts +45 -0
- package/dist/utils/logger.js +66 -0
- package/package.json +58 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authentication via Browser OAuth
|
|
3
|
+
*
|
|
4
|
+
* Flow:
|
|
5
|
+
* 1. CLI starts local HTTP server on available port
|
|
6
|
+
* 2. Opens browser to server's /api/auth/cli endpoint with redirect URL
|
|
7
|
+
* 3. User authenticates via Google OAuth
|
|
8
|
+
* 4. Server redirects back to localhost with JWT token
|
|
9
|
+
* 5. CLI stores token in credentials file
|
|
10
|
+
*/
|
|
11
|
+
interface AuthResult {
|
|
12
|
+
success: boolean;
|
|
13
|
+
token?: string;
|
|
14
|
+
email?: string;
|
|
15
|
+
name?: string;
|
|
16
|
+
error?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Perform browser-based OAuth login
|
|
20
|
+
*/
|
|
21
|
+
export declare function browserLogin(serverUrl?: string): Promise<AuthResult>;
|
|
22
|
+
/**
|
|
23
|
+
* Check if user is logged in
|
|
24
|
+
*/
|
|
25
|
+
export declare function isLoggedIn(): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Get stored credentials
|
|
28
|
+
*/
|
|
29
|
+
export declare function getCredentials(): import("./config.js").Credentials | null;
|
|
30
|
+
/**
|
|
31
|
+
* Logout - remove stored credentials
|
|
32
|
+
*/
|
|
33
|
+
export declare function logout(): void;
|
|
34
|
+
export {};
|
package/dist/lib/auth.js
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authentication via Browser OAuth
|
|
3
|
+
*
|
|
4
|
+
* Flow:
|
|
5
|
+
* 1. CLI starts local HTTP server on available port
|
|
6
|
+
* 2. Opens browser to server's /api/auth/cli endpoint with redirect URL
|
|
7
|
+
* 3. User authenticates via Google OAuth
|
|
8
|
+
* 4. Server redirects back to localhost with JWT token
|
|
9
|
+
* 5. CLI stores token in credentials file
|
|
10
|
+
*/
|
|
11
|
+
import http from 'http';
|
|
12
|
+
import { URL } from 'url';
|
|
13
|
+
import open from 'open';
|
|
14
|
+
import { saveCredentials, loadCredentials, deleteCredentials, getServerUrl, } from './config.js';
|
|
15
|
+
// Port range to try for local OAuth callback server
|
|
16
|
+
const PORT_RANGE_START = 9876;
|
|
17
|
+
const PORT_RANGE_END = 9900;
|
|
18
|
+
/**
|
|
19
|
+
* Find an available port in the range
|
|
20
|
+
*/
|
|
21
|
+
async function findAvailablePort() {
|
|
22
|
+
for (let port = PORT_RANGE_START; port <= PORT_RANGE_END; port++) {
|
|
23
|
+
try {
|
|
24
|
+
await new Promise((resolve, reject) => {
|
|
25
|
+
const server = http.createServer();
|
|
26
|
+
server.listen(port, () => {
|
|
27
|
+
server.close();
|
|
28
|
+
resolve();
|
|
29
|
+
});
|
|
30
|
+
server.on('error', reject);
|
|
31
|
+
});
|
|
32
|
+
return port;
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
// Port in use, try next
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
throw new Error(`No available port in range ${PORT_RANGE_START}-${PORT_RANGE_END}`);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Start local server and wait for OAuth callback
|
|
42
|
+
*/
|
|
43
|
+
function startCallbackServer(port) {
|
|
44
|
+
return new Promise((resolve) => {
|
|
45
|
+
let timeoutId;
|
|
46
|
+
let resolved = false;
|
|
47
|
+
const cleanup = (result) => {
|
|
48
|
+
if (resolved)
|
|
49
|
+
return;
|
|
50
|
+
resolved = true;
|
|
51
|
+
clearTimeout(timeoutId);
|
|
52
|
+
server.close();
|
|
53
|
+
resolve(result);
|
|
54
|
+
};
|
|
55
|
+
const server = http.createServer((req, res) => {
|
|
56
|
+
const url = new URL(req.url || '/', `http://localhost:${port}`);
|
|
57
|
+
// Handle callback
|
|
58
|
+
if (url.pathname === '/callback') {
|
|
59
|
+
const token = url.searchParams.get('token');
|
|
60
|
+
const email = url.searchParams.get('email');
|
|
61
|
+
const name = url.searchParams.get('name');
|
|
62
|
+
const error = url.searchParams.get('error');
|
|
63
|
+
// Send response to browser
|
|
64
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
65
|
+
if (token) {
|
|
66
|
+
res.end(`
|
|
67
|
+
<!DOCTYPE html>
|
|
68
|
+
<html>
|
|
69
|
+
<head>
|
|
70
|
+
<title>OpenCodeSpaces - Login Successful</title>
|
|
71
|
+
<style>
|
|
72
|
+
body { font-family: system-ui, sans-serif; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background: #0f172a; color: #f1f5f9; }
|
|
73
|
+
.container { text-align: center; padding: 2rem; }
|
|
74
|
+
h1 { color: #22c55e; margin-bottom: 0.5rem; }
|
|
75
|
+
p { color: #94a3b8; }
|
|
76
|
+
</style>
|
|
77
|
+
</head>
|
|
78
|
+
<body>
|
|
79
|
+
<div class="container">
|
|
80
|
+
<h1>✓ Login Successful</h1>
|
|
81
|
+
<p>You can close this window and return to your terminal.</p>
|
|
82
|
+
</div>
|
|
83
|
+
</body>
|
|
84
|
+
</html>
|
|
85
|
+
`);
|
|
86
|
+
// Close server after short delay to ensure response is sent
|
|
87
|
+
setTimeout(() => {
|
|
88
|
+
cleanup({ success: true, token, email: email || undefined, name: name || undefined });
|
|
89
|
+
}, 100);
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
res.end(`
|
|
93
|
+
<!DOCTYPE html>
|
|
94
|
+
<html>
|
|
95
|
+
<head>
|
|
96
|
+
<title>OpenCodeSpaces - Login Failed</title>
|
|
97
|
+
<style>
|
|
98
|
+
body { font-family: system-ui, sans-serif; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background: #0f172a; color: #f1f5f9; }
|
|
99
|
+
.container { text-align: center; padding: 2rem; }
|
|
100
|
+
h1 { color: #ef4444; margin-bottom: 0.5rem; }
|
|
101
|
+
p { color: #94a3b8; }
|
|
102
|
+
</style>
|
|
103
|
+
</head>
|
|
104
|
+
<body>
|
|
105
|
+
<div class="container">
|
|
106
|
+
<h1>✗ Login Failed</h1>
|
|
107
|
+
<p>${error || 'Unknown error occurred'}</p>
|
|
108
|
+
</div>
|
|
109
|
+
</body>
|
|
110
|
+
</html>
|
|
111
|
+
`);
|
|
112
|
+
setTimeout(() => {
|
|
113
|
+
cleanup({ success: false, error: error || 'Login failed' });
|
|
114
|
+
}, 100);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
// Unknown path
|
|
119
|
+
res.writeHead(404);
|
|
120
|
+
res.end('Not found');
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
server.listen(port);
|
|
124
|
+
// Timeout after 5 minutes
|
|
125
|
+
timeoutId = setTimeout(() => {
|
|
126
|
+
cleanup({ success: false, error: 'Login timed out' });
|
|
127
|
+
}, 5 * 60 * 1000);
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Perform browser-based OAuth login
|
|
132
|
+
*/
|
|
133
|
+
export async function browserLogin(serverUrl) {
|
|
134
|
+
const server = getServerUrl(serverUrl);
|
|
135
|
+
const port = await findAvailablePort();
|
|
136
|
+
const redirectUrl = `http://localhost:${port}/callback`;
|
|
137
|
+
// Build auth URL
|
|
138
|
+
const authUrl = `${server}/api/auth/cli?redirect=${encodeURIComponent(redirectUrl)}`;
|
|
139
|
+
// Start callback server
|
|
140
|
+
const authPromise = startCallbackServer(port);
|
|
141
|
+
// Open browser
|
|
142
|
+
await open(authUrl);
|
|
143
|
+
// Wait for callback
|
|
144
|
+
const result = await authPromise;
|
|
145
|
+
// Save credentials if successful
|
|
146
|
+
if (result.success && result.token) {
|
|
147
|
+
saveCredentials({
|
|
148
|
+
server,
|
|
149
|
+
token: result.token,
|
|
150
|
+
email: result.email,
|
|
151
|
+
name: result.name,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
return result;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Check if user is logged in
|
|
158
|
+
*/
|
|
159
|
+
export function isLoggedIn() {
|
|
160
|
+
const creds = loadCredentials();
|
|
161
|
+
return !!creds?.token;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Get stored credentials
|
|
165
|
+
*/
|
|
166
|
+
export function getCredentials() {
|
|
167
|
+
return loadCredentials();
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Logout - remove stored credentials
|
|
171
|
+
*/
|
|
172
|
+
export function logout() {
|
|
173
|
+
deleteCredentials();
|
|
174
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration Management
|
|
3
|
+
*
|
|
4
|
+
* Priority: CLI argument > Environment variable > Config file > Default
|
|
5
|
+
*/
|
|
6
|
+
export declare const DEFAULT_IGNORES: string[];
|
|
7
|
+
export interface Config {
|
|
8
|
+
server?: string;
|
|
9
|
+
ignores?: string[];
|
|
10
|
+
}
|
|
11
|
+
export interface Credentials {
|
|
12
|
+
server: string;
|
|
13
|
+
token: string;
|
|
14
|
+
email?: string;
|
|
15
|
+
name?: string;
|
|
16
|
+
expiresAt?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Ensure config directory exists
|
|
20
|
+
*/
|
|
21
|
+
export declare function ensureConfigDir(): void;
|
|
22
|
+
/**
|
|
23
|
+
* Get config directory path
|
|
24
|
+
*/
|
|
25
|
+
export declare function getConfigDir(): string;
|
|
26
|
+
/**
|
|
27
|
+
* Get keys directory path
|
|
28
|
+
*/
|
|
29
|
+
export declare function getKeysDir(): string;
|
|
30
|
+
/**
|
|
31
|
+
* Load configuration
|
|
32
|
+
*/
|
|
33
|
+
export declare function loadConfig(): Config;
|
|
34
|
+
/**
|
|
35
|
+
* Save configuration
|
|
36
|
+
*/
|
|
37
|
+
export declare function saveConfig(config: Config): void;
|
|
38
|
+
/**
|
|
39
|
+
* Get server URL with priority:
|
|
40
|
+
* 1. CLI argument (serverArg)
|
|
41
|
+
* 2. Environment variable (OPENCODESPACES_SERVER)
|
|
42
|
+
* 3. Config file (server key)
|
|
43
|
+
* 4. Credentials file (server from last login)
|
|
44
|
+
* 5. Default
|
|
45
|
+
*/
|
|
46
|
+
export declare function getServerUrl(serverArg?: string): string;
|
|
47
|
+
/**
|
|
48
|
+
* Load stored credentials
|
|
49
|
+
*/
|
|
50
|
+
export declare function loadCredentials(): Credentials | null;
|
|
51
|
+
/**
|
|
52
|
+
* Save credentials
|
|
53
|
+
*/
|
|
54
|
+
export declare function saveCredentials(credentials: Credentials): void;
|
|
55
|
+
/**
|
|
56
|
+
* Delete credentials
|
|
57
|
+
*/
|
|
58
|
+
export declare function deleteCredentials(): void;
|
|
59
|
+
/**
|
|
60
|
+
* Get ignore list from config or defaults
|
|
61
|
+
*/
|
|
62
|
+
export declare function getIgnoreList(): string[];
|
|
63
|
+
/**
|
|
64
|
+
* Save SSH key for a session
|
|
65
|
+
*/
|
|
66
|
+
export declare function saveSessionKey(sessionId: string, privateKey: string): string;
|
|
67
|
+
/**
|
|
68
|
+
* Delete SSH key for a session
|
|
69
|
+
*/
|
|
70
|
+
export declare function deleteSessionKey(sessionId: string): void;
|
|
71
|
+
/**
|
|
72
|
+
* Get SSH key path for a session
|
|
73
|
+
*/
|
|
74
|
+
export declare function getSessionKeyPath(sessionId: string): string;
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration Management
|
|
3
|
+
*
|
|
4
|
+
* Priority: CLI argument > Environment variable > Config file > Default
|
|
5
|
+
*/
|
|
6
|
+
import fs from 'fs';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import os from 'os';
|
|
9
|
+
const CONFIG_DIR = path.join(os.homedir(), '.opencodespaces');
|
|
10
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
11
|
+
const CREDENTIALS_FILE = path.join(CONFIG_DIR, 'credentials.json');
|
|
12
|
+
const KEYS_DIR = path.join(CONFIG_DIR, 'keys');
|
|
13
|
+
// Default server URL
|
|
14
|
+
const DEFAULT_SERVER = 'https://app.opencodespaces.dev';
|
|
15
|
+
// Default ignore patterns for sync
|
|
16
|
+
export const DEFAULT_IGNORES = [
|
|
17
|
+
'node_modules/',
|
|
18
|
+
'.git/',
|
|
19
|
+
'.DS_Store',
|
|
20
|
+
'*.log',
|
|
21
|
+
'dist/',
|
|
22
|
+
'build/',
|
|
23
|
+
'coverage/',
|
|
24
|
+
'.next/',
|
|
25
|
+
'__pycache__/',
|
|
26
|
+
'venv/',
|
|
27
|
+
'.venv/',
|
|
28
|
+
'.turbo/',
|
|
29
|
+
'.cache/',
|
|
30
|
+
];
|
|
31
|
+
/**
|
|
32
|
+
* Ensure config directory exists
|
|
33
|
+
*/
|
|
34
|
+
export function ensureConfigDir() {
|
|
35
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
36
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
37
|
+
}
|
|
38
|
+
if (!fs.existsSync(KEYS_DIR)) {
|
|
39
|
+
fs.mkdirSync(KEYS_DIR, { recursive: true });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Get config directory path
|
|
44
|
+
*/
|
|
45
|
+
export function getConfigDir() {
|
|
46
|
+
return CONFIG_DIR;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Get keys directory path
|
|
50
|
+
*/
|
|
51
|
+
export function getKeysDir() {
|
|
52
|
+
return KEYS_DIR;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Load configuration
|
|
56
|
+
*/
|
|
57
|
+
export function loadConfig() {
|
|
58
|
+
try {
|
|
59
|
+
if (fs.existsSync(CONFIG_FILE)) {
|
|
60
|
+
const content = fs.readFileSync(CONFIG_FILE, 'utf-8');
|
|
61
|
+
return JSON.parse(content);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
// Ignore parse errors
|
|
66
|
+
}
|
|
67
|
+
return {};
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Save configuration
|
|
71
|
+
*/
|
|
72
|
+
export function saveConfig(config) {
|
|
73
|
+
ensureConfigDir();
|
|
74
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Get server URL with priority:
|
|
78
|
+
* 1. CLI argument (serverArg)
|
|
79
|
+
* 2. Environment variable (OPENCODESPACES_SERVER)
|
|
80
|
+
* 3. Config file (server key)
|
|
81
|
+
* 4. Credentials file (server from last login)
|
|
82
|
+
* 5. Default
|
|
83
|
+
*/
|
|
84
|
+
export function getServerUrl(serverArg) {
|
|
85
|
+
// 1. CLI argument
|
|
86
|
+
if (serverArg) {
|
|
87
|
+
return normalizeServerUrl(serverArg);
|
|
88
|
+
}
|
|
89
|
+
// 2. Environment variable
|
|
90
|
+
const envServer = process.env.OPENCODESPACES_SERVER;
|
|
91
|
+
if (envServer) {
|
|
92
|
+
return normalizeServerUrl(envServer);
|
|
93
|
+
}
|
|
94
|
+
// 3. Config file
|
|
95
|
+
const config = loadConfig();
|
|
96
|
+
if (config.server) {
|
|
97
|
+
return normalizeServerUrl(config.server);
|
|
98
|
+
}
|
|
99
|
+
// 4. Credentials file (use last logged-in server)
|
|
100
|
+
const creds = loadCredentials();
|
|
101
|
+
if (creds?.server) {
|
|
102
|
+
return normalizeServerUrl(creds.server);
|
|
103
|
+
}
|
|
104
|
+
// 5. Default
|
|
105
|
+
return DEFAULT_SERVER;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Normalize server URL (add https:// if missing, remove trailing slash)
|
|
109
|
+
*/
|
|
110
|
+
function normalizeServerUrl(url) {
|
|
111
|
+
let normalized = url.trim();
|
|
112
|
+
// Add protocol if missing
|
|
113
|
+
if (!normalized.startsWith('http://') && !normalized.startsWith('https://')) {
|
|
114
|
+
normalized = 'https://' + normalized;
|
|
115
|
+
}
|
|
116
|
+
// Remove trailing slash
|
|
117
|
+
if (normalized.endsWith('/')) {
|
|
118
|
+
normalized = normalized.slice(0, -1);
|
|
119
|
+
}
|
|
120
|
+
return normalized;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Load stored credentials
|
|
124
|
+
*/
|
|
125
|
+
export function loadCredentials() {
|
|
126
|
+
try {
|
|
127
|
+
if (fs.existsSync(CREDENTIALS_FILE)) {
|
|
128
|
+
const content = fs.readFileSync(CREDENTIALS_FILE, 'utf-8');
|
|
129
|
+
return JSON.parse(content);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
// Ignore parse errors
|
|
134
|
+
}
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Save credentials
|
|
139
|
+
*/
|
|
140
|
+
export function saveCredentials(credentials) {
|
|
141
|
+
ensureConfigDir();
|
|
142
|
+
fs.writeFileSync(CREDENTIALS_FILE, JSON.stringify(credentials, null, 2), {
|
|
143
|
+
mode: 0o600, // Restrict to owner only
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Delete credentials
|
|
148
|
+
*/
|
|
149
|
+
export function deleteCredentials() {
|
|
150
|
+
if (fs.existsSync(CREDENTIALS_FILE)) {
|
|
151
|
+
fs.unlinkSync(CREDENTIALS_FILE);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Get ignore list from config or defaults
|
|
156
|
+
*/
|
|
157
|
+
export function getIgnoreList() {
|
|
158
|
+
const config = loadConfig();
|
|
159
|
+
return config.ignores || DEFAULT_IGNORES;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Save SSH key for a session
|
|
163
|
+
*/
|
|
164
|
+
export function saveSessionKey(sessionId, privateKey) {
|
|
165
|
+
ensureConfigDir();
|
|
166
|
+
const keyPath = path.join(KEYS_DIR, sessionId);
|
|
167
|
+
fs.writeFileSync(keyPath, privateKey, { mode: 0o600 });
|
|
168
|
+
return keyPath;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Delete SSH key for a session
|
|
172
|
+
*/
|
|
173
|
+
export function deleteSessionKey(sessionId) {
|
|
174
|
+
const keyPath = path.join(KEYS_DIR, sessionId);
|
|
175
|
+
if (fs.existsSync(keyPath)) {
|
|
176
|
+
fs.unlinkSync(keyPath);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Get SSH key path for a session
|
|
181
|
+
*/
|
|
182
|
+
export function getSessionKeyPath(sessionId) {
|
|
183
|
+
return path.join(KEYS_DIR, sessionId);
|
|
184
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logger utility with colored output
|
|
3
|
+
*/
|
|
4
|
+
export declare const logger: {
|
|
5
|
+
/**
|
|
6
|
+
* Info message (blue)
|
|
7
|
+
*/
|
|
8
|
+
info(message: string): void;
|
|
9
|
+
/**
|
|
10
|
+
* Success message (green)
|
|
11
|
+
*/
|
|
12
|
+
success(message: string): void;
|
|
13
|
+
/**
|
|
14
|
+
* Warning message (yellow)
|
|
15
|
+
*/
|
|
16
|
+
warn(message: string): void;
|
|
17
|
+
/**
|
|
18
|
+
* Error message (red)
|
|
19
|
+
*/
|
|
20
|
+
error(message: string): void;
|
|
21
|
+
/**
|
|
22
|
+
* Plain message (no prefix)
|
|
23
|
+
*/
|
|
24
|
+
log(message: string): void;
|
|
25
|
+
/**
|
|
26
|
+
* Dimmed message (for secondary info)
|
|
27
|
+
*/
|
|
28
|
+
dim(message: string): void;
|
|
29
|
+
/**
|
|
30
|
+
* Bold message
|
|
31
|
+
*/
|
|
32
|
+
bold(message: string): void;
|
|
33
|
+
/**
|
|
34
|
+
* Sync status arrow (up = local to remote)
|
|
35
|
+
*/
|
|
36
|
+
syncUp(message: string): void;
|
|
37
|
+
/**
|
|
38
|
+
* Sync status arrow (down = remote to local)
|
|
39
|
+
*/
|
|
40
|
+
syncDown(message: string): void;
|
|
41
|
+
/**
|
|
42
|
+
* Sync active indicator
|
|
43
|
+
*/
|
|
44
|
+
syncActive(message: string): void;
|
|
45
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logger utility with colored output
|
|
3
|
+
*/
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
export const logger = {
|
|
6
|
+
/**
|
|
7
|
+
* Info message (blue)
|
|
8
|
+
*/
|
|
9
|
+
info(message) {
|
|
10
|
+
console.log(chalk.blue('ℹ'), message);
|
|
11
|
+
},
|
|
12
|
+
/**
|
|
13
|
+
* Success message (green)
|
|
14
|
+
*/
|
|
15
|
+
success(message) {
|
|
16
|
+
console.log(chalk.green('✓'), message);
|
|
17
|
+
},
|
|
18
|
+
/**
|
|
19
|
+
* Warning message (yellow)
|
|
20
|
+
*/
|
|
21
|
+
warn(message) {
|
|
22
|
+
console.log(chalk.yellow('⚠'), message);
|
|
23
|
+
},
|
|
24
|
+
/**
|
|
25
|
+
* Error message (red)
|
|
26
|
+
*/
|
|
27
|
+
error(message) {
|
|
28
|
+
console.error(chalk.red('✗'), message);
|
|
29
|
+
},
|
|
30
|
+
/**
|
|
31
|
+
* Plain message (no prefix)
|
|
32
|
+
*/
|
|
33
|
+
log(message) {
|
|
34
|
+
console.log(message);
|
|
35
|
+
},
|
|
36
|
+
/**
|
|
37
|
+
* Dimmed message (for secondary info)
|
|
38
|
+
*/
|
|
39
|
+
dim(message) {
|
|
40
|
+
console.log(chalk.dim(message));
|
|
41
|
+
},
|
|
42
|
+
/**
|
|
43
|
+
* Bold message
|
|
44
|
+
*/
|
|
45
|
+
bold(message) {
|
|
46
|
+
console.log(chalk.bold(message));
|
|
47
|
+
},
|
|
48
|
+
/**
|
|
49
|
+
* Sync status arrow (up = local to remote)
|
|
50
|
+
*/
|
|
51
|
+
syncUp(message) {
|
|
52
|
+
console.log(chalk.cyan('↑'), message);
|
|
53
|
+
},
|
|
54
|
+
/**
|
|
55
|
+
* Sync status arrow (down = remote to local)
|
|
56
|
+
*/
|
|
57
|
+
syncDown(message) {
|
|
58
|
+
console.log(chalk.cyan('↓'), message);
|
|
59
|
+
},
|
|
60
|
+
/**
|
|
61
|
+
* Sync active indicator
|
|
62
|
+
*/
|
|
63
|
+
syncActive(message) {
|
|
64
|
+
console.log(chalk.cyan('⟳'), message);
|
|
65
|
+
},
|
|
66
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "opencodespaces",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI for OpenCodeSpaces - Connect your local IDE to cloud sessions",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"opencodespaces": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"dev": "tsc -w",
|
|
12
|
+
"start": "node dist/index.js",
|
|
13
|
+
"prepublishOnly": "npm run build"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"keywords": [
|
|
19
|
+
"opencodespaces",
|
|
20
|
+
"cli",
|
|
21
|
+
"ide",
|
|
22
|
+
"sync",
|
|
23
|
+
"mutagen",
|
|
24
|
+
"ssh",
|
|
25
|
+
"cloud-development",
|
|
26
|
+
"remote-development",
|
|
27
|
+
"vscode",
|
|
28
|
+
"jetbrains"
|
|
29
|
+
],
|
|
30
|
+
"author": "Johann Zelger <j.zelger@techdivision.com>",
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "git+https://github.com/techdivision-rnd/opencodespaces.git",
|
|
35
|
+
"directory": "packages/cli"
|
|
36
|
+
},
|
|
37
|
+
"bugs": {
|
|
38
|
+
"url": "https://github.com/techdivision-rnd/opencodespaces/issues"
|
|
39
|
+
},
|
|
40
|
+
"homepage": "https://github.com/techdivision-rnd/opencodespaces/tree/master/packages/cli#readme",
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": ">=18.0.0"
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"chalk": "^5.3.0",
|
|
46
|
+
"commander": "^12.1.0",
|
|
47
|
+
"inquirer": "^12.4.0",
|
|
48
|
+
"open": "^10.1.0",
|
|
49
|
+
"ora": "^8.1.1",
|
|
50
|
+
"ws": "^8.18.0"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@types/inquirer": "^9.0.7",
|
|
54
|
+
"@types/node": "^22.10.2",
|
|
55
|
+
"@types/ws": "^8.5.13",
|
|
56
|
+
"typescript": "^5.7.2"
|
|
57
|
+
}
|
|
58
|
+
}
|