sessioncast-cli 2.3.1 → 2.4.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.
@@ -214,19 +214,81 @@ async function logout() {
214
214
  (0, config_1.clearAuth)();
215
215
  console.log(chalk_1.default.green('\u2713 Logged out successfully!'));
216
216
  }
217
+ function formatExpiry(expiresAt) {
218
+ const now = Date.now();
219
+ if (now > expiresAt) {
220
+ return chalk_1.default.red('expired');
221
+ }
222
+ const remaining = expiresAt - now;
223
+ const hours = Math.floor(remaining / (1000 * 60 * 60));
224
+ const minutes = Math.floor((remaining % (1000 * 60 * 60)) / (1000 * 60));
225
+ if (hours > 24) {
226
+ const days = Math.floor(hours / 24);
227
+ return chalk_1.default.green(`${days}d ${hours % 24}h remaining`);
228
+ }
229
+ if (hours > 0) {
230
+ return chalk_1.default.green(`${hours}h ${minutes}m remaining`);
231
+ }
232
+ return chalk_1.default.yellow(`${minutes}m remaining`);
233
+ }
217
234
  function status() {
218
235
  const accessToken = (0, config_1.getAccessToken)();
219
- if ((0, config_1.isLoggedIn)()) {
220
- console.log(chalk_1.default.green('\u2713 Logged in'));
221
- if (accessToken) {
222
- console.log(chalk_1.default.gray(' Auth method: OAuth'));
223
- }
224
- else {
225
- console.log(chalk_1.default.gray(' Auth method: API Key / Agent Token'));
226
- }
236
+ const rawAccessToken = (0, config_1.getRawAccessToken)();
237
+ const agentToken = (0, config_1.getAgentToken)();
238
+ const apiKey = (0, config_1.getApiKey)();
239
+ const refreshToken = (0, config_1.getRefreshToken)();
240
+ const machineId = (0, config_1.getMachineId)();
241
+ const expiresAt = (0, config_1.getTokenExpiresAt)();
242
+ console.log('');
243
+ if (!(0, config_1.isLoggedIn)()) {
244
+ console.log(chalk_1.default.yellow(' Not logged in'));
245
+ console.log(chalk_1.default.gray(' Run: sessioncast login\n'));
246
+ return;
247
+ }
248
+ console.log(chalk_1.default.green.bold(' \u2713 Logged in\n'));
249
+ // Auth method
250
+ if (accessToken) {
251
+ console.log(chalk_1.default.gray(' Auth method: ') + 'OAuth');
252
+ }
253
+ else if (rawAccessToken) {
254
+ console.log(chalk_1.default.gray(' Auth method: ') + chalk_1.default.yellow('OAuth (token expired)'));
255
+ }
256
+ else if (apiKey) {
257
+ console.log(chalk_1.default.gray(' Auth method: ') + 'API Key');
227
258
  }
228
259
  else {
229
- console.log(chalk_1.default.yellow('Not logged in'));
230
- console.log(chalk_1.default.gray('Run: sessioncast login'));
260
+ console.log(chalk_1.default.gray(' Auth method: ') + 'Agent Token');
261
+ }
262
+ if (machineId) {
263
+ console.log(chalk_1.default.gray(' Machine ID: ') + machineId);
264
+ }
265
+ console.log('');
266
+ // Tokens
267
+ console.log(chalk_1.default.bold(' Tokens'));
268
+ if (agentToken) {
269
+ console.log(chalk_1.default.gray(' Agent Token: ') + agentToken);
270
+ }
271
+ if (rawAccessToken) {
272
+ const expiryStr = expiresAt ? ` (${formatExpiry(expiresAt)})` : '';
273
+ console.log(chalk_1.default.gray(' Access Token: ') + rawAccessToken + expiryStr);
274
+ }
275
+ if (refreshToken) {
276
+ console.log(chalk_1.default.gray(' Refresh Token: ') + refreshToken);
277
+ }
278
+ if (apiKey) {
279
+ console.log(chalk_1.default.gray(' API Key: ') + apiKey);
280
+ }
281
+ if (!agentToken && !rawAccessToken && !refreshToken && !apiKey) {
282
+ console.log(chalk_1.default.gray(' (none)'));
231
283
  }
284
+ console.log('');
285
+ // Endpoints
286
+ console.log(chalk_1.default.bold(' Endpoints'));
287
+ console.log(chalk_1.default.gray(' API: ') + (0, config_1.getApiUrl)());
288
+ console.log(chalk_1.default.gray(' Auth: ') + (0, config_1.getAuthUrl)());
289
+ console.log(chalk_1.default.gray(' Relay: ') + (0, config_1.getRelayUrl)());
290
+ console.log('');
291
+ // Config path
292
+ console.log(chalk_1.default.gray(` Config: ${(0, config_1.getConfigPath)()}`));
293
+ console.log('');
232
294
  }
package/dist/config.d.ts CHANGED
@@ -33,4 +33,7 @@ export declare function clearAuth(): void;
33
33
  export declare function isLoggedIn(): boolean;
34
34
  export declare function hasSeenWelcome(): boolean;
35
35
  export declare function setSeenWelcome(): void;
36
+ export declare function getConfigPath(): string;
37
+ export declare function getRawAccessToken(): string | undefined;
38
+ export declare function getTokenExpiresAt(): number | undefined;
36
39
  export default config;
package/dist/config.js CHANGED
@@ -24,6 +24,9 @@ exports.clearAuth = clearAuth;
24
24
  exports.isLoggedIn = isLoggedIn;
25
25
  exports.hasSeenWelcome = hasSeenWelcome;
26
26
  exports.setSeenWelcome = setSeenWelcome;
27
+ exports.getConfigPath = getConfigPath;
28
+ exports.getRawAccessToken = getRawAccessToken;
29
+ exports.getTokenExpiresAt = getTokenExpiresAt;
27
30
  const conf_1 = __importDefault(require("conf"));
28
31
  const config = new conf_1.default({
29
32
  projectName: 'sessioncast',
@@ -110,4 +113,15 @@ function hasSeenWelcome() {
110
113
  function setSeenWelcome() {
111
114
  config.set('hasSeenWelcome', true);
112
115
  }
116
+ // Config file path
117
+ function getConfigPath() {
118
+ return config.path;
119
+ }
120
+ // Raw token access (bypass expiry check, for status display)
121
+ function getRawAccessToken() {
122
+ return config.get('accessToken');
123
+ }
124
+ function getTokenExpiresAt() {
125
+ return config.get('tokenExpiresAt');
126
+ }
113
127
  exports.default = config;
package/dist/index.js CHANGED
@@ -185,7 +185,7 @@ program
185
185
  // Status command
186
186
  program
187
187
  .command('status')
188
- .description('Check login status')
188
+ .description('Check login status and token info')
189
189
  .action(login_1.status);
190
190
  // Agents command
191
191
  program
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sessioncast-cli",
3
- "version": "2.3.1",
3
+ "version": "2.4.0",
4
4
  "description": "SessionCast CLI - Control your agents from anywhere",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -1,12 +0,0 @@
1
- export declare class TunnelHandler {
2
- private port;
3
- private host;
4
- private activeRequests;
5
- private readonly MAX_CONCURRENT;
6
- private readonly MAX_RESPONSE_SIZE;
7
- private readonly CHUNK_SIZE;
8
- constructor(port: number, host?: string);
9
- handleRequest(requestId: string, method: string, path: string, queryString: string, headers: Record<string, string>, body: string | undefined, sendResponse: (msg: any) => void): Promise<void>;
10
- private getBase64ChunkSize;
11
- private makeHttpRequest;
12
- }
@@ -1,180 +0,0 @@
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.TunnelHandler = void 0;
37
- const http = __importStar(require("http"));
38
- const debug_1 = require("./debug");
39
- const ALLOWED_HOSTS = ['localhost', '127.0.0.1', '::1'];
40
- class TunnelHandler {
41
- constructor(port, host = 'localhost') {
42
- this.activeRequests = 0;
43
- this.MAX_CONCURRENT = 10;
44
- this.MAX_RESPONSE_SIZE = 5 * 1024 * 1024; // 5MB
45
- this.CHUNK_SIZE = 200 * 1024; // 200KB before base64
46
- this.port = port;
47
- this.host = host;
48
- }
49
- async handleRequest(requestId, method, path, queryString, headers, body, sendResponse) {
50
- // Check concurrent limit
51
- if (this.activeRequests >= this.MAX_CONCURRENT) {
52
- sendResponse({
53
- requestId,
54
- statusCode: 503,
55
- headers: { 'content-type': 'text/plain' },
56
- body: Buffer.from('Too many concurrent requests').toString('base64'),
57
- });
58
- return;
59
- }
60
- // Validate host (SSRF prevention)
61
- if (!ALLOWED_HOSTS.includes(this.host)) {
62
- sendResponse({
63
- requestId,
64
- statusCode: 403,
65
- headers: { 'content-type': 'text/plain' },
66
- body: Buffer.from('Forbidden: only localhost connections allowed').toString('base64'),
67
- });
68
- return;
69
- }
70
- this.activeRequests++;
71
- try {
72
- const fullPath = queryString ? `${path}?${queryString}` : path;
73
- const requestBody = body ? Buffer.from(body, 'base64') : undefined;
74
- (0, debug_1.debugLog)('TUNNEL', `${method} ${fullPath} -> ${this.host}:${this.port}`);
75
- const { statusCode, responseHeaders, responseBody } = await this.makeHttpRequest(method, fullPath, headers, requestBody);
76
- const bodyBase64 = responseBody.toString('base64');
77
- if (responseBody.length > this.CHUNK_SIZE) {
78
- // Split into chunks
79
- const totalChunks = Math.ceil(bodyBase64.length / this.getBase64ChunkSize());
80
- for (let i = 0; i < totalChunks; i++) {
81
- const start = i * this.getBase64ChunkSize();
82
- const end = Math.min(start + this.getBase64ChunkSize(), bodyBase64.length);
83
- const chunk = bodyBase64.slice(start, end);
84
- const isFinal = i === totalChunks - 1;
85
- sendResponse({
86
- requestId,
87
- statusCode,
88
- headers: responseHeaders,
89
- body: chunk,
90
- chunkIndex: i,
91
- isFinal,
92
- chunked: true,
93
- });
94
- }
95
- }
96
- else {
97
- sendResponse({
98
- requestId,
99
- statusCode,
100
- headers: responseHeaders,
101
- body: bodyBase64,
102
- });
103
- }
104
- }
105
- catch (error) {
106
- (0, debug_1.debugLog)('TUNNEL', `Error: ${error.message}`);
107
- sendResponse({
108
- requestId,
109
- statusCode: 502,
110
- headers: { 'content-type': 'text/plain' },
111
- body: Buffer.from(`Bad Gateway: ${error.message}`).toString('base64'),
112
- });
113
- }
114
- finally {
115
- this.activeRequests--;
116
- }
117
- }
118
- getBase64ChunkSize() {
119
- // base64 encoding expands data by ~4/3, so chunk size in base64 chars
120
- // for 200KB raw data is approximately 200*1024*4/3 ≈ 273KB base64
121
- return Math.ceil(this.CHUNK_SIZE * 4 / 3);
122
- }
123
- makeHttpRequest(method, path, headers, body) {
124
- return new Promise((resolve, reject) => {
125
- // Build headers, removing hop-by-hop headers that shouldn't be forwarded
126
- const forwardHeaders = {
127
- ...headers,
128
- host: `${this.host}:${this.port}`,
129
- };
130
- delete forwardHeaders['connection'];
131
- delete forwardHeaders['keep-alive'];
132
- delete forwardHeaders['transfer-encoding'];
133
- const options = {
134
- hostname: this.host,
135
- port: this.port,
136
- path,
137
- method,
138
- headers: forwardHeaders,
139
- timeout: 30000,
140
- };
141
- const req = http.request(options, (res) => {
142
- const chunks = [];
143
- let totalSize = 0;
144
- res.on('data', (chunk) => {
145
- totalSize += chunk.length;
146
- if (totalSize > this.MAX_RESPONSE_SIZE) {
147
- req.destroy();
148
- reject(new Error(`Response exceeds maximum size of ${this.MAX_RESPONSE_SIZE} bytes`));
149
- return;
150
- }
151
- chunks.push(chunk);
152
- });
153
- res.on('end', () => {
154
- const responseHeaders = {};
155
- for (const [key, value] of Object.entries(res.headers)) {
156
- if (value) {
157
- responseHeaders[key] = Array.isArray(value) ? value.join(', ') : value;
158
- }
159
- }
160
- resolve({
161
- statusCode: res.statusCode || 502,
162
- responseHeaders,
163
- responseBody: Buffer.concat(chunks),
164
- });
165
- });
166
- res.on('error', reject);
167
- });
168
- req.on('error', reject);
169
- req.on('timeout', () => {
170
- req.destroy();
171
- reject(new Error('Request timed out'));
172
- });
173
- if (body) {
174
- req.write(body);
175
- }
176
- req.end();
177
- });
178
- }
179
- }
180
- exports.TunnelHandler = TunnelHandler;