sessioncast-cli 1.0.0 → 1.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 +65 -40
- package/dist/agent/session-handler.d.ts +1 -0
- package/dist/agent/session-handler.js +42 -0
- package/dist/agent/tmux-executor.d.ts +66 -0
- package/dist/agent/tmux-executor.js +368 -0
- package/dist/agent/tmux.d.ts +9 -1
- package/dist/agent/tmux.js +52 -76
- package/dist/agent/websocket.d.ts +2 -8
- package/dist/agent/websocket.js +78 -14
- package/dist/autopilot/index.d.ts +94 -0
- package/dist/autopilot/index.js +322 -0
- package/dist/autopilot/mission-analyzer.d.ts +27 -0
- package/dist/autopilot/mission-analyzer.js +232 -0
- package/dist/autopilot/project-detector.d.ts +12 -0
- package/dist/autopilot/project-detector.js +326 -0
- package/dist/autopilot/source-scanner.d.ts +26 -0
- package/dist/autopilot/source-scanner.js +285 -0
- package/dist/autopilot/speckit-generator.d.ts +60 -0
- package/dist/autopilot/speckit-generator.js +511 -0
- package/dist/autopilot/types.d.ts +110 -0
- package/dist/autopilot/types.js +6 -0
- package/dist/autopilot/workflow-generator.d.ts +33 -0
- package/dist/autopilot/workflow-generator.js +278 -0
- package/dist/commands/autopilot.d.ts +30 -0
- package/dist/commands/autopilot.js +262 -0
- package/dist/commands/login.d.ts +2 -1
- package/dist/commands/login.js +199 -8
- package/dist/commands/project.d.ts +1 -1
- package/dist/commands/project.js +4 -13
- package/dist/config.d.ts +20 -0
- package/dist/config.js +69 -2
- package/dist/index.js +7 -47
- package/dist/project/executor.d.ts +8 -53
- package/dist/project/executor.js +64 -520
- package/dist/project/manager.d.ts +0 -13
- package/dist/project/manager.js +0 -107
- package/dist/project/relay-client.d.ts +18 -68
- package/dist/project/relay-client.js +134 -130
- package/dist/project/types.d.ts +5 -0
- package/dist/utils/fileUtils.d.ts +28 -0
- package/dist/utils/fileUtils.js +159 -0
- package/dist/utils/oauthServer.d.ts +18 -0
- package/dist/utils/oauthServer.js +244 -0
- package/dist/utils/pkce.d.ts +16 -0
- package/dist/utils/pkce.js +73 -0
- package/package.json +5 -14
- package/LICENSE +0 -21
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.sanitizeFilename = sanitizeFilename;
|
|
37
|
+
exports.getUploadDirectory = getUploadDirectory;
|
|
38
|
+
exports.handleUploadChunk = handleUploadChunk;
|
|
39
|
+
exports.cleanupStaleUploads = cleanupStaleUploads;
|
|
40
|
+
const fs = __importStar(require("fs"));
|
|
41
|
+
const path = __importStar(require("path"));
|
|
42
|
+
// Track ongoing uploads
|
|
43
|
+
const pendingUploads = new Map();
|
|
44
|
+
// Cleanup stale uploads after 5 minutes
|
|
45
|
+
const UPLOAD_TIMEOUT_MS = 5 * 60 * 1000;
|
|
46
|
+
/**
|
|
47
|
+
* Sanitize filename to prevent path traversal attacks
|
|
48
|
+
*/
|
|
49
|
+
function sanitizeFilename(filename) {
|
|
50
|
+
// Remove any path components
|
|
51
|
+
const basename = path.basename(filename);
|
|
52
|
+
// Remove potentially dangerous characters
|
|
53
|
+
return basename.replace(/[<>:"/\\|?*\x00-\x1f]/g, '_');
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Get the working directory for file uploads
|
|
57
|
+
* Priority: SESSIONCAST_UPLOAD_DIR env var > current working directory
|
|
58
|
+
*/
|
|
59
|
+
function getUploadDirectory() {
|
|
60
|
+
return process.env.SESSIONCAST_UPLOAD_DIR || process.cwd();
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Handle file upload chunk
|
|
64
|
+
* Returns upload result when all chunks are received, null otherwise
|
|
65
|
+
*/
|
|
66
|
+
async function handleUploadChunk(sessionId, meta, payload) {
|
|
67
|
+
const filename = sanitizeFilename(meta.filename);
|
|
68
|
+
const uploadKey = `${sessionId}:${filename}`;
|
|
69
|
+
const chunkIndex = parseInt(meta.chunkIndex, 10);
|
|
70
|
+
const totalChunks = parseInt(meta.totalChunks, 10);
|
|
71
|
+
const size = parseInt(meta.size, 10);
|
|
72
|
+
// Validate parameters
|
|
73
|
+
if (isNaN(chunkIndex) || isNaN(totalChunks) || isNaN(size)) {
|
|
74
|
+
return { success: false, error: 'Invalid upload metadata' };
|
|
75
|
+
}
|
|
76
|
+
// Check file size limit (10MB)
|
|
77
|
+
const MAX_FILE_SIZE = 10 * 1024 * 1024;
|
|
78
|
+
if (size > MAX_FILE_SIZE) {
|
|
79
|
+
return { success: false, error: `File too large. Max size: ${MAX_FILE_SIZE / 1024 / 1024}MB` };
|
|
80
|
+
}
|
|
81
|
+
// Get or create upload state
|
|
82
|
+
let state = pendingUploads.get(uploadKey);
|
|
83
|
+
if (!state) {
|
|
84
|
+
state = {
|
|
85
|
+
filename,
|
|
86
|
+
totalChunks,
|
|
87
|
+
receivedChunks: new Map(),
|
|
88
|
+
size,
|
|
89
|
+
mimeType: meta.mimeType,
|
|
90
|
+
createdAt: Date.now(),
|
|
91
|
+
};
|
|
92
|
+
pendingUploads.set(uploadKey, state);
|
|
93
|
+
}
|
|
94
|
+
// Decode and store chunk
|
|
95
|
+
try {
|
|
96
|
+
const chunkBuffer = Buffer.from(payload, 'base64');
|
|
97
|
+
state.receivedChunks.set(chunkIndex, chunkBuffer);
|
|
98
|
+
console.log(`[FileUpload] Received chunk ${chunkIndex + 1}/${totalChunks} for ${filename}`);
|
|
99
|
+
}
|
|
100
|
+
catch (e) {
|
|
101
|
+
pendingUploads.delete(uploadKey);
|
|
102
|
+
return { success: false, error: 'Failed to decode chunk data' };
|
|
103
|
+
}
|
|
104
|
+
// Check if all chunks received
|
|
105
|
+
if (state.receivedChunks.size === totalChunks) {
|
|
106
|
+
try {
|
|
107
|
+
// Assemble file from chunks
|
|
108
|
+
const chunks = [];
|
|
109
|
+
for (let i = 0; i < totalChunks; i++) {
|
|
110
|
+
const chunk = state.receivedChunks.get(i);
|
|
111
|
+
if (!chunk) {
|
|
112
|
+
throw new Error(`Missing chunk ${i}`);
|
|
113
|
+
}
|
|
114
|
+
chunks.push(chunk);
|
|
115
|
+
}
|
|
116
|
+
const fileBuffer = Buffer.concat(chunks);
|
|
117
|
+
// Determine save path
|
|
118
|
+
const uploadDir = getUploadDirectory();
|
|
119
|
+
let savePath = path.join(uploadDir, filename);
|
|
120
|
+
// Handle filename collision
|
|
121
|
+
if (fs.existsSync(savePath)) {
|
|
122
|
+
const ext = path.extname(filename);
|
|
123
|
+
const basename = path.basename(filename, ext);
|
|
124
|
+
let counter = 1;
|
|
125
|
+
while (fs.existsSync(savePath)) {
|
|
126
|
+
savePath = path.join(uploadDir, `${basename}_${counter}${ext}`);
|
|
127
|
+
counter++;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// Write file
|
|
131
|
+
fs.writeFileSync(savePath, fileBuffer);
|
|
132
|
+
console.log(`[FileUpload] File saved: ${savePath}`);
|
|
133
|
+
// Cleanup
|
|
134
|
+
pendingUploads.delete(uploadKey);
|
|
135
|
+
return { success: true, path: savePath };
|
|
136
|
+
}
|
|
137
|
+
catch (e) {
|
|
138
|
+
pendingUploads.delete(uploadKey);
|
|
139
|
+
const errorMsg = e instanceof Error ? e.message : 'Unknown error';
|
|
140
|
+
return { success: false, error: `Failed to save file: ${errorMsg}` };
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
// Not all chunks received yet
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Cleanup stale uploads periodically
|
|
148
|
+
*/
|
|
149
|
+
function cleanupStaleUploads() {
|
|
150
|
+
const now = Date.now();
|
|
151
|
+
for (const [key, state] of pendingUploads.entries()) {
|
|
152
|
+
if (now - state.createdAt > UPLOAD_TIMEOUT_MS) {
|
|
153
|
+
console.log(`[FileUpload] Cleaning up stale upload: ${state.filename}`);
|
|
154
|
+
pendingUploads.delete(key);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
// Run cleanup every minute
|
|
159
|
+
setInterval(cleanupStaleUploads, 60 * 1000);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
interface CallbackResult {
|
|
2
|
+
code?: string;
|
|
3
|
+
state?: string;
|
|
4
|
+
error?: string;
|
|
5
|
+
errorDescription?: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Start a temporary local HTTP server to receive OAuth callback
|
|
9
|
+
* @param port Port number to listen on
|
|
10
|
+
* @param timeoutMs Timeout in milliseconds (default: 5 minutes)
|
|
11
|
+
* @returns Promise that resolves with the authorization code
|
|
12
|
+
*/
|
|
13
|
+
export declare function startCallbackServer(port?: number, timeoutMs?: number): Promise<CallbackResult>;
|
|
14
|
+
/**
|
|
15
|
+
* Find an available port
|
|
16
|
+
*/
|
|
17
|
+
export declare function findAvailablePort(startPort?: number): Promise<number>;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.startCallbackServer = startCallbackServer;
|
|
37
|
+
exports.findAvailablePort = findAvailablePort;
|
|
38
|
+
const http = __importStar(require("http"));
|
|
39
|
+
const url = __importStar(require("url"));
|
|
40
|
+
/**
|
|
41
|
+
* Start a temporary local HTTP server to receive OAuth callback
|
|
42
|
+
* @param port Port number to listen on
|
|
43
|
+
* @param timeoutMs Timeout in milliseconds (default: 5 minutes)
|
|
44
|
+
* @returns Promise that resolves with the authorization code
|
|
45
|
+
*/
|
|
46
|
+
function startCallbackServer(port = 9876, timeoutMs = 300000) {
|
|
47
|
+
return new Promise((resolve, reject) => {
|
|
48
|
+
const server = http.createServer((req, res) => {
|
|
49
|
+
const parsedUrl = url.parse(req.url || '', true);
|
|
50
|
+
if (parsedUrl.pathname === '/callback') {
|
|
51
|
+
const query = parsedUrl.query;
|
|
52
|
+
// Build result
|
|
53
|
+
const result = {
|
|
54
|
+
code: query.code,
|
|
55
|
+
state: query.state,
|
|
56
|
+
error: query.error,
|
|
57
|
+
errorDescription: query.error_description,
|
|
58
|
+
};
|
|
59
|
+
// Send response to browser
|
|
60
|
+
if (result.error) {
|
|
61
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
62
|
+
res.end(getErrorPage(result.error, result.errorDescription));
|
|
63
|
+
}
|
|
64
|
+
else if (result.code) {
|
|
65
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
66
|
+
res.end(getSuccessPage());
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
70
|
+
res.end(getErrorPage('missing_code', 'No authorization code received'));
|
|
71
|
+
}
|
|
72
|
+
// Close server and resolve
|
|
73
|
+
server.close();
|
|
74
|
+
resolve(result);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
res.writeHead(404);
|
|
78
|
+
res.end('Not Found');
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
// Handle server errors
|
|
82
|
+
server.on('error', (err) => {
|
|
83
|
+
if (err.code === 'EADDRINUSE') {
|
|
84
|
+
reject(new Error(`Port ${port} is already in use. Please try again.`));
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
reject(err);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
// Start listening
|
|
91
|
+
server.listen(port, '127.0.0.1', () => {
|
|
92
|
+
// console.log(`OAuth callback server listening on http://127.0.0.1:${port}/callback`);
|
|
93
|
+
});
|
|
94
|
+
// Set timeout
|
|
95
|
+
const timeout = setTimeout(() => {
|
|
96
|
+
server.close();
|
|
97
|
+
reject(new Error('Login timed out. Please try again.'));
|
|
98
|
+
}, timeoutMs);
|
|
99
|
+
// Clear timeout on successful resolution
|
|
100
|
+
server.on('close', () => {
|
|
101
|
+
clearTimeout(timeout);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Find an available port
|
|
107
|
+
*/
|
|
108
|
+
async function findAvailablePort(startPort = 9876) {
|
|
109
|
+
return new Promise((resolve, reject) => {
|
|
110
|
+
const server = http.createServer();
|
|
111
|
+
server.on('error', (err) => {
|
|
112
|
+
if (err.code === 'EADDRINUSE') {
|
|
113
|
+
// Try next port
|
|
114
|
+
server.close();
|
|
115
|
+
resolve(findAvailablePort(startPort + 1));
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
reject(err);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
server.listen(startPort, '127.0.0.1', () => {
|
|
122
|
+
server.close(() => {
|
|
123
|
+
resolve(startPort);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
function getSuccessPage() {
|
|
129
|
+
return `
|
|
130
|
+
<!DOCTYPE html>
|
|
131
|
+
<html>
|
|
132
|
+
<head>
|
|
133
|
+
<meta charset="utf-8">
|
|
134
|
+
<title>SessionCast - Login Successful</title>
|
|
135
|
+
<style>
|
|
136
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
137
|
+
body {
|
|
138
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
139
|
+
background: linear-gradient(135deg, #0f0f23 0%, #1a1a3e 100%);
|
|
140
|
+
color: #fff;
|
|
141
|
+
min-height: 100vh;
|
|
142
|
+
display: flex;
|
|
143
|
+
align-items: center;
|
|
144
|
+
justify-content: center;
|
|
145
|
+
}
|
|
146
|
+
.container {
|
|
147
|
+
text-align: center;
|
|
148
|
+
padding: 40px;
|
|
149
|
+
}
|
|
150
|
+
.icon {
|
|
151
|
+
font-size: 64px;
|
|
152
|
+
margin-bottom: 24px;
|
|
153
|
+
}
|
|
154
|
+
h1 {
|
|
155
|
+
font-size: 28px;
|
|
156
|
+
margin-bottom: 16px;
|
|
157
|
+
color: #22c55e;
|
|
158
|
+
}
|
|
159
|
+
p {
|
|
160
|
+
color: #a1a1aa;
|
|
161
|
+
font-size: 16px;
|
|
162
|
+
margin-bottom: 24px;
|
|
163
|
+
}
|
|
164
|
+
.hint {
|
|
165
|
+
background: rgba(255,255,255,0.1);
|
|
166
|
+
padding: 12px 24px;
|
|
167
|
+
border-radius: 8px;
|
|
168
|
+
font-family: monospace;
|
|
169
|
+
font-size: 14px;
|
|
170
|
+
}
|
|
171
|
+
</style>
|
|
172
|
+
</head>
|
|
173
|
+
<body>
|
|
174
|
+
<div class="container">
|
|
175
|
+
<div class="icon">✅</div>
|
|
176
|
+
<h1>Login Successful!</h1>
|
|
177
|
+
<p>You can close this window and return to your terminal.</p>
|
|
178
|
+
<div class="hint">sessioncast agent</div>
|
|
179
|
+
</div>
|
|
180
|
+
<script>
|
|
181
|
+
// Auto-close after 3 seconds
|
|
182
|
+
setTimeout(() => window.close(), 3000);
|
|
183
|
+
</script>
|
|
184
|
+
</body>
|
|
185
|
+
</html>
|
|
186
|
+
`;
|
|
187
|
+
}
|
|
188
|
+
function getErrorPage(error, description) {
|
|
189
|
+
return `
|
|
190
|
+
<!DOCTYPE html>
|
|
191
|
+
<html>
|
|
192
|
+
<head>
|
|
193
|
+
<meta charset="utf-8">
|
|
194
|
+
<title>SessionCast - Login Failed</title>
|
|
195
|
+
<style>
|
|
196
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
197
|
+
body {
|
|
198
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
199
|
+
background: linear-gradient(135deg, #0f0f23 0%, #1a1a3e 100%);
|
|
200
|
+
color: #fff;
|
|
201
|
+
min-height: 100vh;
|
|
202
|
+
display: flex;
|
|
203
|
+
align-items: center;
|
|
204
|
+
justify-content: center;
|
|
205
|
+
}
|
|
206
|
+
.container {
|
|
207
|
+
text-align: center;
|
|
208
|
+
padding: 40px;
|
|
209
|
+
}
|
|
210
|
+
.icon {
|
|
211
|
+
font-size: 64px;
|
|
212
|
+
margin-bottom: 24px;
|
|
213
|
+
}
|
|
214
|
+
h1 {
|
|
215
|
+
font-size: 28px;
|
|
216
|
+
margin-bottom: 16px;
|
|
217
|
+
color: #ef4444;
|
|
218
|
+
}
|
|
219
|
+
p {
|
|
220
|
+
color: #a1a1aa;
|
|
221
|
+
font-size: 16px;
|
|
222
|
+
margin-bottom: 16px;
|
|
223
|
+
}
|
|
224
|
+
.error-detail {
|
|
225
|
+
background: rgba(239, 68, 68, 0.1);
|
|
226
|
+
padding: 12px 24px;
|
|
227
|
+
border-radius: 8px;
|
|
228
|
+
font-family: monospace;
|
|
229
|
+
font-size: 14px;
|
|
230
|
+
color: #fca5a5;
|
|
231
|
+
}
|
|
232
|
+
</style>
|
|
233
|
+
</head>
|
|
234
|
+
<body>
|
|
235
|
+
<div class="container">
|
|
236
|
+
<div class="icon">❌</div>
|
|
237
|
+
<h1>Login Failed</h1>
|
|
238
|
+
<p>${description || 'An error occurred during authentication.'}</p>
|
|
239
|
+
<div class="error-detail">${error}</div>
|
|
240
|
+
</div>
|
|
241
|
+
</body>
|
|
242
|
+
</html>
|
|
243
|
+
`;
|
|
244
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate a cryptographically random code verifier for PKCE
|
|
3
|
+
* @returns Base64URL encoded random string (43-128 characters)
|
|
4
|
+
*/
|
|
5
|
+
export declare function generateCodeVerifier(): string;
|
|
6
|
+
/**
|
|
7
|
+
* Generate code challenge from code verifier using SHA-256
|
|
8
|
+
* @param codeVerifier The code verifier string
|
|
9
|
+
* @returns Base64URL encoded SHA-256 hash
|
|
10
|
+
*/
|
|
11
|
+
export declare function generateCodeChallenge(codeVerifier: string): string;
|
|
12
|
+
/**
|
|
13
|
+
* Generate a random state parameter for OAuth
|
|
14
|
+
* @returns Random hex string
|
|
15
|
+
*/
|
|
16
|
+
export declare function generateState(): string;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.generateCodeVerifier = generateCodeVerifier;
|
|
37
|
+
exports.generateCodeChallenge = generateCodeChallenge;
|
|
38
|
+
exports.generateState = generateState;
|
|
39
|
+
const crypto = __importStar(require("crypto"));
|
|
40
|
+
/**
|
|
41
|
+
* Generate a cryptographically random code verifier for PKCE
|
|
42
|
+
* @returns Base64URL encoded random string (43-128 characters)
|
|
43
|
+
*/
|
|
44
|
+
function generateCodeVerifier() {
|
|
45
|
+
const buffer = crypto.randomBytes(32);
|
|
46
|
+
return base64URLEncode(buffer);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Generate code challenge from code verifier using SHA-256
|
|
50
|
+
* @param codeVerifier The code verifier string
|
|
51
|
+
* @returns Base64URL encoded SHA-256 hash
|
|
52
|
+
*/
|
|
53
|
+
function generateCodeChallenge(codeVerifier) {
|
|
54
|
+
const hash = crypto.createHash('sha256').update(codeVerifier).digest();
|
|
55
|
+
return base64URLEncode(hash);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Generate a random state parameter for OAuth
|
|
59
|
+
* @returns Random hex string
|
|
60
|
+
*/
|
|
61
|
+
function generateState() {
|
|
62
|
+
return crypto.randomBytes(16).toString('hex');
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Base64URL encode a buffer (RFC 4648)
|
|
66
|
+
*/
|
|
67
|
+
function base64URLEncode(buffer) {
|
|
68
|
+
return buffer
|
|
69
|
+
.toString('base64')
|
|
70
|
+
.replace(/\+/g, '-')
|
|
71
|
+
.replace(/\//g, '_')
|
|
72
|
+
.replace(/=/g, '');
|
|
73
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sessioncast-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "SessionCast CLI - Control your agents from anywhere",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -17,23 +17,13 @@
|
|
|
17
17
|
"tmux",
|
|
18
18
|
"cli",
|
|
19
19
|
"terminal",
|
|
20
|
-
"remote"
|
|
21
|
-
"agent",
|
|
22
|
-
"websocket"
|
|
20
|
+
"remote"
|
|
23
21
|
],
|
|
24
|
-
"author":
|
|
25
|
-
"name": "SessionCast",
|
|
26
|
-
"email": "devload@sessioncast.io"
|
|
27
|
-
},
|
|
22
|
+
"author": "SessionCast",
|
|
28
23
|
"license": "MIT",
|
|
29
|
-
"homepage": "https://sessioncast.io",
|
|
30
|
-
"bugs": {
|
|
31
|
-
"email": "devload@sessioncast.io"
|
|
32
|
-
},
|
|
33
24
|
"type": "commonjs",
|
|
34
25
|
"files": [
|
|
35
|
-
"dist"
|
|
36
|
-
"README.md"
|
|
26
|
+
"dist"
|
|
37
27
|
],
|
|
38
28
|
"dependencies": {
|
|
39
29
|
"chalk": "^4.1.2",
|
|
@@ -50,6 +40,7 @@
|
|
|
50
40
|
"@types/node": "^22.10.2",
|
|
51
41
|
"@types/node-fetch": "^2.6.13",
|
|
52
42
|
"@types/ws": "^8.5.13",
|
|
43
|
+
"puppeteer": "^24.34.0",
|
|
53
44
|
"ts-node": "^10.9.2",
|
|
54
45
|
"typescript": "^5.7.2"
|
|
55
46
|
},
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2024 SessionCast
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|