@startanaicompany/cli 1.4.19 → 1.4.21

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/bin/saac.js CHANGED
@@ -123,6 +123,17 @@ gitCommand
123
123
  .description('Disconnect a Git account')
124
124
  .action(git.disconnect);
125
125
 
126
+ gitCommand
127
+ .command('repos <git_host>')
128
+ .description('List repositories from a connected Git host')
129
+ .option('-p, --page <number>', 'Page number', '1')
130
+ .option('-n, --per-page <number>', 'Results per page (max: 100)', '20')
131
+ .option('-s, --sort <type>', 'Sort order: updated, created, name', 'updated')
132
+ .option('-v, --visibility <type>', 'Filter: all, public, private', 'all')
133
+ .option('-c, --commits', 'Include latest commit info')
134
+ .option('--json', 'Output as JSON')
135
+ .action(git.repos);
136
+
126
137
  // Application management
127
138
  program
128
139
  .command('init')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@startanaicompany/cli",
3
- "version": "1.4.19",
3
+ "version": "1.4.21",
4
4
  "description": "Official CLI for StartAnAiCompany.com - Deploy AI recruitment sites with ease",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -3,6 +3,7 @@
3
3
  */
4
4
 
5
5
  const oauth = require('../lib/oauth');
6
+ const api = require('../lib/api');
6
7
  const { isAuthenticated, getUser } = require('../lib/config');
7
8
  const logger = require('../lib/logger');
8
9
  const { table } = require('table');
@@ -247,8 +248,169 @@ async function disconnect(host) {
247
248
  }
248
249
  }
249
250
 
251
+ /**
252
+ * List repositories from a connected Git host
253
+ */
254
+ async function repos(gitHost, options) {
255
+ try {
256
+ // Check authentication
257
+ if (!isAuthenticated()) {
258
+ logger.error('Not logged in');
259
+ logger.newline();
260
+ logger.info('Run:');
261
+ logger.log(' saac login -e <email> -k <api-key>');
262
+ process.exit(1);
263
+ }
264
+
265
+ if (!gitHost) {
266
+ logger.error('Git host is required');
267
+ logger.newline();
268
+ logger.info('Usage:');
269
+ logger.log(' saac git repos <host>');
270
+ logger.newline();
271
+ logger.info('Examples:');
272
+ logger.log(' saac git repos git.startanaicompany.com');
273
+ logger.log(' saac git repos github.com');
274
+ logger.log(' saac git repos github.com --commits');
275
+ logger.log(' saac git repos github.com --visibility private');
276
+ logger.newline();
277
+ logger.info('To see connected accounts:');
278
+ logger.log(' saac git list');
279
+ process.exit(1);
280
+ }
281
+
282
+ const spin = logger.spinner(`Fetching repositories from ${gitHost}...`).start();
283
+
284
+ try {
285
+ const result = await api.listGitRepositories(gitHost, {
286
+ page: options.page ? parseInt(options.page, 10) : 1,
287
+ perPage: options.perPage ? parseInt(options.perPage, 10) : 20,
288
+ sort: options.sort || 'updated',
289
+ visibility: options.visibility || 'all',
290
+ includeCommits: options.commits || false,
291
+ });
292
+
293
+ spin.succeed(`Found ${result.count} repositories on ${result.git_host}`);
294
+
295
+ // If JSON output requested, just dump and exit
296
+ if (options.json) {
297
+ console.log(JSON.stringify(result, null, 2));
298
+ return;
299
+ }
300
+
301
+ if (result.count === 0) {
302
+ logger.newline();
303
+ logger.warn('No repositories found');
304
+ logger.newline();
305
+ logger.info('Make sure you have repositories on this Git host');
306
+ return;
307
+ }
308
+
309
+ logger.newline();
310
+ logger.section(`Repositories on ${result.git_host} (${result.username})`);
311
+ logger.newline();
312
+
313
+ if (options.commits) {
314
+ // Display with commit info
315
+ const data = [
316
+ ['NAME', 'VISIBILITY', 'LAST COMMIT'],
317
+ ];
318
+
319
+ result.repositories.forEach((repo) => {
320
+ const visibility = repo.private ? logger.chalk.yellow('private') : logger.chalk.green('public');
321
+ let commitInfo = logger.chalk.dim('No commits');
322
+
323
+ if (repo.latestCommit) {
324
+ const commit = repo.latestCommit;
325
+ const sha = commit.sha.substring(0, 7);
326
+ const message = commit.message.substring(0, 30);
327
+ const date = new Date(commit.date);
328
+ const now = new Date();
329
+ const diffMs = now - date;
330
+ const diffMins = Math.floor(diffMs / 60000);
331
+ const diffHours = Math.floor(diffMs / 3600000);
332
+ const diffDays = Math.floor(diffMs / 86400000);
333
+
334
+ let timeStr;
335
+ if (diffMins < 60) {
336
+ timeStr = `${diffMins}m ago`;
337
+ } else if (diffHours < 24) {
338
+ timeStr = `${diffHours}h ago`;
339
+ } else {
340
+ timeStr = `${diffDays}d ago`;
341
+ }
342
+
343
+ commitInfo = `${logger.chalk.dim(sha)} ${message} ${logger.chalk.dim(`(${timeStr})`)}`;
344
+ }
345
+
346
+ data.push([
347
+ repo.name,
348
+ visibility,
349
+ commitInfo,
350
+ ]);
351
+ });
352
+
353
+ console.log(table(data));
354
+ } else {
355
+ // Display without commit info
356
+ const data = [
357
+ ['NAME', 'VISIBILITY', 'UPDATED', 'BRANCH', 'LANGUAGE'],
358
+ ];
359
+
360
+ result.repositories.forEach((repo) => {
361
+ const visibility = repo.private ? logger.chalk.yellow('private') : logger.chalk.green('public');
362
+ const updated = new Date(repo.updatedAt).toLocaleString();
363
+ const language = repo.language || logger.chalk.dim('N/A');
364
+
365
+ data.push([
366
+ repo.name,
367
+ visibility,
368
+ updated,
369
+ repo.defaultBranch,
370
+ language,
371
+ ]);
372
+ });
373
+
374
+ console.log(table(data));
375
+ }
376
+
377
+ logger.info(`Showing ${result.count} repositories (page ${result.page})`);
378
+
379
+ logger.newline();
380
+ logger.info('Options:');
381
+ logger.log(' --commits Include latest commit info');
382
+ logger.log(' --visibility <type> Filter: all, public, private');
383
+ logger.log(' --page <number> Page number');
384
+ logger.log(' --per-page <number> Results per page (max: 100)');
385
+ logger.log(' --json Output as JSON');
386
+
387
+ } catch (error) {
388
+ spin.fail('Failed to fetch repositories');
389
+
390
+ if (error.response?.status === 404) {
391
+ logger.newline();
392
+ logger.error(`Not connected to ${gitHost}`);
393
+ logger.newline();
394
+ logger.info('Connect with:');
395
+ logger.log(` saac git connect ${gitHost}`);
396
+ logger.newline();
397
+ logger.info('To see connected accounts:');
398
+ logger.log(' saac git list');
399
+ } else {
400
+ throw error;
401
+ }
402
+ process.exit(1);
403
+ }
404
+
405
+ } catch (error) {
406
+ logger.error(error.response?.data?.message || error.message);
407
+ process.exit(1);
408
+ }
409
+ }
410
+
250
411
  module.exports = {
251
412
  connect,
252
413
  list,
253
414
  disconnect,
415
+ repos,
254
416
  };
@@ -188,11 +188,20 @@ async function getRuntimeLogs(applicationUuid, applicationName, options) {
188
188
  logger.newline();
189
189
 
190
190
  // Display logs
191
- if (result.logs && result.logs.length > 0) {
192
- result.logs.forEach(log => {
193
- console.log(log);
194
- });
191
+ if (result.logs) {
192
+ if (Array.isArray(result.logs)) {
193
+ // Logs is an array
194
+ result.logs.forEach(log => {
195
+ console.log(log);
196
+ });
197
+ } else if (typeof result.logs === 'string') {
198
+ // Logs is a string (most common format from backend)
199
+ console.log(result.logs);
200
+ } else {
201
+ logger.warn('Unexpected log format');
202
+ }
195
203
  } else if (typeof result === 'string') {
204
+ // Entire result is a string
196
205
  console.log(result);
197
206
  } else {
198
207
  logger.warn('No logs available');
package/src/lib/api.js CHANGED
@@ -290,6 +290,35 @@ async function getExecutionHistory(uuid, params = {}) {
290
290
  return response.data;
291
291
  }
292
292
 
293
+ /**
294
+ * List repositories from a connected Git host
295
+ * @param {string} gitHost - Git host domain (e.g., 'github.com', 'git.startanaicompany.com')
296
+ * @param {object} options - Query options
297
+ * @param {number} options.page - Page number for pagination (default: 1)
298
+ * @param {number} options.perPage - Results per page (default: 100, max: 100)
299
+ * @param {string} options.sort - Sort order: 'updated', 'created', 'name' (default: 'updated')
300
+ * @param {string} options.visibility - Filter: 'all', 'public', 'private' (default: 'all')
301
+ * @param {boolean} options.includeCommits - Include latest commit info (default: false)
302
+ * @returns {Promise<object>} - Repository listing response
303
+ */
304
+ async function listGitRepositories(gitHost, options = {}) {
305
+ const client = createClient();
306
+
307
+ // Build query parameters
308
+ const params = {};
309
+ if (options.page) params.page = options.page;
310
+ if (options.perPage) params.per_page = options.perPage;
311
+ if (options.sort) params.sort = options.sort;
312
+ if (options.visibility) params.visibility = options.visibility;
313
+ if (options.includeCommits) params.include_commits = true;
314
+
315
+ // URL encode the git host
316
+ const encodedHost = encodeURIComponent(gitHost);
317
+
318
+ const response = await client.get(`/git/connections/${encodedHost}/repos`, { params });
319
+ return response.data;
320
+ }
321
+
293
322
  module.exports = {
294
323
  createClient,
295
324
  login,
@@ -315,4 +344,5 @@ module.exports = {
315
344
  getDeploymentLogs,
316
345
  executeCommand,
317
346
  getExecutionHistory,
347
+ listGitRepositories,
318
348
  };