@startanaicompany/cli 1.4.20 → 1.5.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/CLAUDE.md +67 -7
- package/README.md +2272 -271
- package/bin/saac.js +11 -0
- package/package.json +1 -1
- package/src/commands/create.js +5 -6
- package/src/commands/delete.js +5 -3
- package/src/commands/deploy.js +6 -4
- package/src/commands/deployments.js +5 -3
- package/src/commands/domain.js +9 -5
- package/src/commands/env.js +9 -5
- package/src/commands/exec.js +9 -5
- package/src/commands/git.js +171 -13
- package/src/commands/init.js +116 -20
- package/src/commands/keys.js +7 -3
- package/src/commands/list.js +5 -6
- package/src/commands/logs.js +5 -3
- package/src/commands/run.js +5 -3
- package/src/commands/sessions.js +4 -5
- package/src/commands/shell.js +5 -3
- package/src/commands/status.js +4 -5
- package/src/commands/update.js +5 -6
- package/src/commands/whoami.js +4 -5
- package/src/lib/api.js +30 -0
- package/src/lib/config.js +47 -0
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
package/src/commands/create.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
const api = require('../lib/api');
|
|
6
|
-
const {
|
|
6
|
+
const { ensureAuthenticated, saveProjectConfig, getUser, getProjectConfig } = require('../lib/config');
|
|
7
7
|
const logger = require('../lib/logger');
|
|
8
8
|
const oauth = require('../lib/oauth');
|
|
9
9
|
const inquirer = require('inquirer');
|
|
@@ -12,12 +12,11 @@ const errorDisplay = require('../lib/errorDisplay');
|
|
|
12
12
|
|
|
13
13
|
async function create(name, options) {
|
|
14
14
|
try {
|
|
15
|
-
// Check authentication
|
|
16
|
-
if (!
|
|
15
|
+
// Check authentication (with auto-login support)
|
|
16
|
+
if (!(await ensureAuthenticated())) {
|
|
17
17
|
logger.error('Not logged in');
|
|
18
|
-
logger.
|
|
19
|
-
logger.info('
|
|
20
|
-
logger.log(' saac login -e <email> -k <api-key>');
|
|
18
|
+
logger.info('Run: saac login -e <email> -k <api-key>');
|
|
19
|
+
logger.info('Or set: SAAC_USER_API_KEY and SAAC_USER_EMAIL');
|
|
21
20
|
process.exit(1);
|
|
22
21
|
}
|
|
23
22
|
|
package/src/commands/delete.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
const api = require('../lib/api');
|
|
6
|
-
const { getProjectConfig,
|
|
6
|
+
const { getProjectConfig, ensureAuthenticated } = require('../lib/config');
|
|
7
7
|
const logger = require('../lib/logger');
|
|
8
8
|
const inquirer = require('inquirer');
|
|
9
9
|
const fs = require('fs');
|
|
@@ -16,8 +16,10 @@ const path = require('path');
|
|
|
16
16
|
async function deleteApp(options) {
|
|
17
17
|
try {
|
|
18
18
|
// Check authentication
|
|
19
|
-
if (!
|
|
20
|
-
logger.error('Not logged in
|
|
19
|
+
if (!(await ensureAuthenticated())) {
|
|
20
|
+
logger.error('Not logged in');
|
|
21
|
+
logger.info('Run: saac login -e <email> -k <api-key>');
|
|
22
|
+
logger.info('Or set: SAAC_USER_API_KEY and SAAC_USER_EMAIL');
|
|
21
23
|
process.exit(1);
|
|
22
24
|
}
|
|
23
25
|
|
package/src/commands/deploy.js
CHANGED
|
@@ -3,15 +3,17 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
const api = require('../lib/api');
|
|
6
|
-
const { getProjectConfig,
|
|
6
|
+
const { getProjectConfig, ensureAuthenticated } = require('../lib/config');
|
|
7
7
|
const logger = require('../lib/logger');
|
|
8
8
|
const errorDisplay = require('../lib/errorDisplay');
|
|
9
9
|
|
|
10
10
|
async function deploy(options) {
|
|
11
11
|
try {
|
|
12
|
-
// Check authentication
|
|
13
|
-
if (!
|
|
14
|
-
logger.error('Not logged in
|
|
12
|
+
// Check authentication (with auto-login support)
|
|
13
|
+
if (!(await ensureAuthenticated())) {
|
|
14
|
+
logger.error('Not logged in');
|
|
15
|
+
logger.info('Run: saac login -e <email> -k <api-key>');
|
|
16
|
+
logger.info('Or set: SAAC_USER_API_KEY and SAAC_USER_EMAIL');
|
|
15
17
|
process.exit(1);
|
|
16
18
|
}
|
|
17
19
|
|
|
@@ -3,15 +3,17 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
const api = require('../lib/api');
|
|
6
|
-
const { getProjectConfig,
|
|
6
|
+
const { getProjectConfig, ensureAuthenticated } = require('../lib/config');
|
|
7
7
|
const logger = require('../lib/logger');
|
|
8
8
|
const { table } = require('table');
|
|
9
9
|
|
|
10
10
|
async function deployments(options) {
|
|
11
11
|
try {
|
|
12
12
|
// Check authentication
|
|
13
|
-
if (!
|
|
14
|
-
logger.error('Not logged in
|
|
13
|
+
if (!(await ensureAuthenticated())) {
|
|
14
|
+
logger.error('Not logged in');
|
|
15
|
+
logger.info('Run: saac login -e <email> -k <api-key>');
|
|
16
|
+
logger.info('Or set: SAAC_USER_API_KEY and SAAC_USER_EMAIL');
|
|
15
17
|
process.exit(1);
|
|
16
18
|
}
|
|
17
19
|
|
package/src/commands/domain.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
const api = require('../lib/api');
|
|
6
|
-
const { getProjectConfig,
|
|
6
|
+
const { getProjectConfig, ensureAuthenticated, saveProjectConfig } = require('../lib/config');
|
|
7
7
|
const logger = require('../lib/logger');
|
|
8
8
|
|
|
9
9
|
/**
|
|
@@ -14,8 +14,10 @@ const logger = require('../lib/logger');
|
|
|
14
14
|
async function set(subdomain, options) {
|
|
15
15
|
try {
|
|
16
16
|
// Check authentication
|
|
17
|
-
if (!
|
|
18
|
-
logger.error('Not logged in
|
|
17
|
+
if (!(await ensureAuthenticated())) {
|
|
18
|
+
logger.error('Not logged in');
|
|
19
|
+
logger.info('Run: saac login -e <email> -k <api-key>');
|
|
20
|
+
logger.info('Or set: SAAC_USER_API_KEY and SAAC_USER_EMAIL');
|
|
19
21
|
process.exit(1);
|
|
20
22
|
}
|
|
21
23
|
|
|
@@ -149,8 +151,10 @@ async function set(subdomain, options) {
|
|
|
149
151
|
async function show() {
|
|
150
152
|
try {
|
|
151
153
|
// Check authentication
|
|
152
|
-
if (!
|
|
153
|
-
logger.error('Not logged in
|
|
154
|
+
if (!(await ensureAuthenticated())) {
|
|
155
|
+
logger.error('Not logged in');
|
|
156
|
+
logger.info('Run: saac login -e <email> -k <api-key>');
|
|
157
|
+
logger.info('Or set: SAAC_USER_API_KEY and SAAC_USER_EMAIL');
|
|
154
158
|
process.exit(1);
|
|
155
159
|
}
|
|
156
160
|
|
package/src/commands/env.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
const api = require('../lib/api');
|
|
6
|
-
const { getProjectConfig,
|
|
6
|
+
const { getProjectConfig, ensureAuthenticated } = require('../lib/config');
|
|
7
7
|
const logger = require('../lib/logger');
|
|
8
8
|
const { table } = require('table');
|
|
9
9
|
|
|
@@ -14,8 +14,10 @@ const { table } = require('table');
|
|
|
14
14
|
async function get(key) {
|
|
15
15
|
try {
|
|
16
16
|
// Check authentication
|
|
17
|
-
if (!
|
|
18
|
-
logger.error('Not logged in
|
|
17
|
+
if (!(await ensureAuthenticated())) {
|
|
18
|
+
logger.error('Not logged in');
|
|
19
|
+
logger.info('Run: saac login -e <email> -k <api-key>');
|
|
20
|
+
logger.info('Or set: SAAC_USER_API_KEY and SAAC_USER_EMAIL');
|
|
19
21
|
process.exit(1);
|
|
20
22
|
}
|
|
21
23
|
|
|
@@ -108,8 +110,10 @@ async function list() {
|
|
|
108
110
|
async function set(vars) {
|
|
109
111
|
try {
|
|
110
112
|
// Check authentication
|
|
111
|
-
if (!
|
|
112
|
-
logger.error('Not logged in
|
|
113
|
+
if (!(await ensureAuthenticated())) {
|
|
114
|
+
logger.error('Not logged in');
|
|
115
|
+
logger.info('Run: saac login -e <email> -k <api-key>');
|
|
116
|
+
logger.info('Or set: SAAC_USER_API_KEY and SAAC_USER_EMAIL');
|
|
113
117
|
process.exit(1);
|
|
114
118
|
}
|
|
115
119
|
|
package/src/commands/exec.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
const api = require('../lib/api');
|
|
6
|
-
const { getProjectConfig,
|
|
6
|
+
const { getProjectConfig, ensureAuthenticated } = require('../lib/config');
|
|
7
7
|
const logger = require('../lib/logger');
|
|
8
8
|
const { table } = require('table');
|
|
9
9
|
|
|
@@ -15,8 +15,10 @@ const { table } = require('table');
|
|
|
15
15
|
async function exec(command, options = {}) {
|
|
16
16
|
try {
|
|
17
17
|
// Check authentication
|
|
18
|
-
if (!
|
|
19
|
-
logger.error('Not logged in
|
|
18
|
+
if (!(await ensureAuthenticated())) {
|
|
19
|
+
logger.error('Not logged in');
|
|
20
|
+
logger.info('Run: saac login -e <email> -k <api-key>');
|
|
21
|
+
logger.info('Or set: SAAC_USER_API_KEY and SAAC_USER_EMAIL');
|
|
20
22
|
process.exit(1);
|
|
21
23
|
}
|
|
22
24
|
|
|
@@ -156,8 +158,10 @@ async function exec(command, options = {}) {
|
|
|
156
158
|
async function history(options = {}) {
|
|
157
159
|
try {
|
|
158
160
|
// Check authentication
|
|
159
|
-
if (!
|
|
160
|
-
logger.error('Not logged in
|
|
161
|
+
if (!(await ensureAuthenticated())) {
|
|
162
|
+
logger.error('Not logged in');
|
|
163
|
+
logger.info('Run: saac login -e <email> -k <api-key>');
|
|
164
|
+
logger.info('Or set: SAAC_USER_API_KEY and SAAC_USER_EMAIL');
|
|
161
165
|
process.exit(1);
|
|
162
166
|
}
|
|
163
167
|
|
package/src/commands/git.js
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
const oauth = require('../lib/oauth');
|
|
6
|
-
const
|
|
6
|
+
const api = require('../lib/api');
|
|
7
|
+
const { ensureAuthenticated, getUser } = require('../lib/config');
|
|
7
8
|
const logger = require('../lib/logger');
|
|
8
9
|
const { table } = require('table');
|
|
9
10
|
const inquirer = require('inquirer');
|
|
@@ -14,11 +15,10 @@ const inquirer = require('inquirer');
|
|
|
14
15
|
async function connect(host) {
|
|
15
16
|
try {
|
|
16
17
|
// Check authentication
|
|
17
|
-
if (!
|
|
18
|
+
if (!(await ensureAuthenticated())) {
|
|
18
19
|
logger.error('Not logged in');
|
|
19
|
-
logger.
|
|
20
|
-
logger.info('
|
|
21
|
-
logger.log(' saac login -e <email> -k <api-key>');
|
|
20
|
+
logger.info('Run: saac login -e <email> -k <api-key>');
|
|
21
|
+
logger.info('Or set: SAAC_USER_API_KEY and SAAC_USER_EMAIL');
|
|
22
22
|
process.exit(1);
|
|
23
23
|
}
|
|
24
24
|
|
|
@@ -115,11 +115,10 @@ async function connect(host) {
|
|
|
115
115
|
async function list() {
|
|
116
116
|
try {
|
|
117
117
|
// Check authentication
|
|
118
|
-
if (!
|
|
118
|
+
if (!(await ensureAuthenticated())) {
|
|
119
119
|
logger.error('Not logged in');
|
|
120
|
-
logger.
|
|
121
|
-
logger.info('
|
|
122
|
-
logger.log(' saac login -e <email> -k <api-key>');
|
|
120
|
+
logger.info('Run: saac login -e <email> -k <api-key>');
|
|
121
|
+
logger.info('Or set: SAAC_USER_API_KEY and SAAC_USER_EMAIL');
|
|
123
122
|
process.exit(1);
|
|
124
123
|
}
|
|
125
124
|
|
|
@@ -192,11 +191,10 @@ async function list() {
|
|
|
192
191
|
async function disconnect(host) {
|
|
193
192
|
try {
|
|
194
193
|
// Check authentication
|
|
195
|
-
if (!
|
|
194
|
+
if (!(await ensureAuthenticated())) {
|
|
196
195
|
logger.error('Not logged in');
|
|
197
|
-
logger.
|
|
198
|
-
logger.info('
|
|
199
|
-
logger.log(' saac login -e <email> -k <api-key>');
|
|
196
|
+
logger.info('Run: saac login -e <email> -k <api-key>');
|
|
197
|
+
logger.info('Or set: SAAC_USER_API_KEY and SAAC_USER_EMAIL');
|
|
200
198
|
process.exit(1);
|
|
201
199
|
}
|
|
202
200
|
|
|
@@ -247,8 +245,168 @@ async function disconnect(host) {
|
|
|
247
245
|
}
|
|
248
246
|
}
|
|
249
247
|
|
|
248
|
+
/**
|
|
249
|
+
* List repositories from a connected Git host
|
|
250
|
+
*/
|
|
251
|
+
async function repos(gitHost, options) {
|
|
252
|
+
try {
|
|
253
|
+
// Check authentication
|
|
254
|
+
if (!(await ensureAuthenticated())) {
|
|
255
|
+
logger.error('Not logged in');
|
|
256
|
+
logger.info('Run: saac login -e <email> -k <api-key>');
|
|
257
|
+
logger.info('Or set: SAAC_USER_API_KEY and SAAC_USER_EMAIL');
|
|
258
|
+
process.exit(1);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (!gitHost) {
|
|
262
|
+
logger.error('Git host is required');
|
|
263
|
+
logger.newline();
|
|
264
|
+
logger.info('Usage:');
|
|
265
|
+
logger.log(' saac git repos <host>');
|
|
266
|
+
logger.newline();
|
|
267
|
+
logger.info('Examples:');
|
|
268
|
+
logger.log(' saac git repos git.startanaicompany.com');
|
|
269
|
+
logger.log(' saac git repos github.com');
|
|
270
|
+
logger.log(' saac git repos github.com --commits');
|
|
271
|
+
logger.log(' saac git repos github.com --visibility private');
|
|
272
|
+
logger.newline();
|
|
273
|
+
logger.info('To see connected accounts:');
|
|
274
|
+
logger.log(' saac git list');
|
|
275
|
+
process.exit(1);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const spin = logger.spinner(`Fetching repositories from ${gitHost}...`).start();
|
|
279
|
+
|
|
280
|
+
try {
|
|
281
|
+
const result = await api.listGitRepositories(gitHost, {
|
|
282
|
+
page: options.page ? parseInt(options.page, 10) : 1,
|
|
283
|
+
perPage: options.perPage ? parseInt(options.perPage, 10) : 20,
|
|
284
|
+
sort: options.sort || 'updated',
|
|
285
|
+
visibility: options.visibility || 'all',
|
|
286
|
+
includeCommits: options.commits || false,
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
spin.succeed(`Found ${result.count} repositories on ${result.git_host}`);
|
|
290
|
+
|
|
291
|
+
// If JSON output requested, just dump and exit
|
|
292
|
+
if (options.json) {
|
|
293
|
+
console.log(JSON.stringify(result, null, 2));
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if (result.count === 0) {
|
|
298
|
+
logger.newline();
|
|
299
|
+
logger.warn('No repositories found');
|
|
300
|
+
logger.newline();
|
|
301
|
+
logger.info('Make sure you have repositories on this Git host');
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
logger.newline();
|
|
306
|
+
logger.section(`Repositories on ${result.git_host} (${result.username})`);
|
|
307
|
+
logger.newline();
|
|
308
|
+
|
|
309
|
+
if (options.commits) {
|
|
310
|
+
// Display with commit info
|
|
311
|
+
const data = [
|
|
312
|
+
['NAME', 'VISIBILITY', 'LAST COMMIT'],
|
|
313
|
+
];
|
|
314
|
+
|
|
315
|
+
result.repositories.forEach((repo) => {
|
|
316
|
+
const visibility = repo.private ? logger.chalk.yellow('private') : logger.chalk.green('public');
|
|
317
|
+
let commitInfo = logger.chalk.dim('No commits');
|
|
318
|
+
|
|
319
|
+
if (repo.latestCommit) {
|
|
320
|
+
const commit = repo.latestCommit;
|
|
321
|
+
const sha = commit.sha.substring(0, 7);
|
|
322
|
+
const message = commit.message.substring(0, 30);
|
|
323
|
+
const date = new Date(commit.date);
|
|
324
|
+
const now = new Date();
|
|
325
|
+
const diffMs = now - date;
|
|
326
|
+
const diffMins = Math.floor(diffMs / 60000);
|
|
327
|
+
const diffHours = Math.floor(diffMs / 3600000);
|
|
328
|
+
const diffDays = Math.floor(diffMs / 86400000);
|
|
329
|
+
|
|
330
|
+
let timeStr;
|
|
331
|
+
if (diffMins < 60) {
|
|
332
|
+
timeStr = `${diffMins}m ago`;
|
|
333
|
+
} else if (diffHours < 24) {
|
|
334
|
+
timeStr = `${diffHours}h ago`;
|
|
335
|
+
} else {
|
|
336
|
+
timeStr = `${diffDays}d ago`;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
commitInfo = `${logger.chalk.dim(sha)} ${message} ${logger.chalk.dim(`(${timeStr})`)}`;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
data.push([
|
|
343
|
+
repo.name,
|
|
344
|
+
visibility,
|
|
345
|
+
commitInfo,
|
|
346
|
+
]);
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
console.log(table(data));
|
|
350
|
+
} else {
|
|
351
|
+
// Display without commit info
|
|
352
|
+
const data = [
|
|
353
|
+
['NAME', 'VISIBILITY', 'UPDATED', 'BRANCH', 'LANGUAGE'],
|
|
354
|
+
];
|
|
355
|
+
|
|
356
|
+
result.repositories.forEach((repo) => {
|
|
357
|
+
const visibility = repo.private ? logger.chalk.yellow('private') : logger.chalk.green('public');
|
|
358
|
+
const updated = new Date(repo.updatedAt).toLocaleString();
|
|
359
|
+
const language = repo.language || logger.chalk.dim('N/A');
|
|
360
|
+
|
|
361
|
+
data.push([
|
|
362
|
+
repo.name,
|
|
363
|
+
visibility,
|
|
364
|
+
updated,
|
|
365
|
+
repo.defaultBranch,
|
|
366
|
+
language,
|
|
367
|
+
]);
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
console.log(table(data));
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
logger.info(`Showing ${result.count} repositories (page ${result.page})`);
|
|
374
|
+
|
|
375
|
+
logger.newline();
|
|
376
|
+
logger.info('Options:');
|
|
377
|
+
logger.log(' --commits Include latest commit info');
|
|
378
|
+
logger.log(' --visibility <type> Filter: all, public, private');
|
|
379
|
+
logger.log(' --page <number> Page number');
|
|
380
|
+
logger.log(' --per-page <number> Results per page (max: 100)');
|
|
381
|
+
logger.log(' --json Output as JSON');
|
|
382
|
+
|
|
383
|
+
} catch (error) {
|
|
384
|
+
spin.fail('Failed to fetch repositories');
|
|
385
|
+
|
|
386
|
+
if (error.response?.status === 404) {
|
|
387
|
+
logger.newline();
|
|
388
|
+
logger.error(`Not connected to ${gitHost}`);
|
|
389
|
+
logger.newline();
|
|
390
|
+
logger.info('Connect with:');
|
|
391
|
+
logger.log(` saac git connect ${gitHost}`);
|
|
392
|
+
logger.newline();
|
|
393
|
+
logger.info('To see connected accounts:');
|
|
394
|
+
logger.log(' saac git list');
|
|
395
|
+
} else {
|
|
396
|
+
throw error;
|
|
397
|
+
}
|
|
398
|
+
process.exit(1);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
} catch (error) {
|
|
402
|
+
logger.error(error.response?.data?.message || error.message);
|
|
403
|
+
process.exit(1);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
250
407
|
module.exports = {
|
|
251
408
|
connect,
|
|
252
409
|
list,
|
|
253
410
|
disconnect,
|
|
411
|
+
repos,
|
|
254
412
|
};
|
package/src/commands/init.js
CHANGED
|
@@ -7,18 +7,20 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
const api = require('../lib/api');
|
|
10
|
-
const {
|
|
10
|
+
const { ensureAuthenticated, saveProjectConfig, getProjectConfig } = require('../lib/config');
|
|
11
11
|
const logger = require('../lib/logger');
|
|
12
12
|
const inquirer = require('inquirer');
|
|
13
|
+
const { execSync } = require('child_process');
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const path = require('path');
|
|
13
16
|
|
|
14
17
|
async function init(options) {
|
|
15
18
|
try {
|
|
16
|
-
// Check authentication
|
|
17
|
-
if (!
|
|
19
|
+
// Check authentication (with auto-login support)
|
|
20
|
+
if (!(await ensureAuthenticated())) {
|
|
18
21
|
logger.error('Not logged in');
|
|
19
|
-
logger.
|
|
20
|
-
logger.info('
|
|
21
|
-
logger.log(' saac login -e <email> -k <api-key>');
|
|
22
|
+
logger.info('Run: saac login -e <email> -k <api-key>');
|
|
23
|
+
logger.info('Or set: SAAC_USER_API_KEY and SAAC_USER_EMAIL');
|
|
22
24
|
process.exit(1);
|
|
23
25
|
}
|
|
24
26
|
|
|
@@ -86,11 +88,58 @@ async function createAndInitialize(options) {
|
|
|
86
88
|
// This would call the create command functionality, then save the config
|
|
87
89
|
}
|
|
88
90
|
|
|
91
|
+
/**
|
|
92
|
+
* Get Git remote URL from current directory
|
|
93
|
+
* @returns {string|null} - Git remote URL or null if not a git repo
|
|
94
|
+
*/
|
|
95
|
+
function getGitRemoteUrl() {
|
|
96
|
+
try {
|
|
97
|
+
// Check if .git directory exists
|
|
98
|
+
if (!fs.existsSync(path.join(process.cwd(), '.git'))) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Get remote.origin.url
|
|
103
|
+
const remoteUrl = execSync('git config --get remote.origin.url', {
|
|
104
|
+
encoding: 'utf8',
|
|
105
|
+
stdio: ['pipe', 'pipe', 'ignore'] // Suppress stderr
|
|
106
|
+
}).trim();
|
|
107
|
+
|
|
108
|
+
return remoteUrl || null;
|
|
109
|
+
} catch (error) {
|
|
110
|
+
// Not a git repo or no remote configured
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Normalize Git URLs for comparison
|
|
117
|
+
* Converts both SSH and HTTPS URLs to a comparable format
|
|
118
|
+
*/
|
|
119
|
+
function normalizeGitUrl(url) {
|
|
120
|
+
if (!url) return '';
|
|
121
|
+
|
|
122
|
+
// Remove .git suffix
|
|
123
|
+
let normalized = url.replace(/\.git$/, '');
|
|
124
|
+
|
|
125
|
+
// Convert SSH to HTTPS-like format for comparison
|
|
126
|
+
// git@github.com:user/repo -> github.com/user/repo
|
|
127
|
+
normalized = normalized.replace(/^git@([^:]+):/, '$1/');
|
|
128
|
+
|
|
129
|
+
// Remove https:// or http://
|
|
130
|
+
normalized = normalized.replace(/^https?:\/\//, '');
|
|
131
|
+
|
|
132
|
+
return normalized.toLowerCase();
|
|
133
|
+
}
|
|
134
|
+
|
|
89
135
|
/**
|
|
90
136
|
* Link an existing application to current directory (interactive)
|
|
91
137
|
*/
|
|
92
138
|
async function linkExistingApplication() {
|
|
93
139
|
|
|
140
|
+
// Try to auto-detect Git repository
|
|
141
|
+
const gitRemoteUrl = getGitRemoteUrl();
|
|
142
|
+
|
|
94
143
|
// Fetch user's applications
|
|
95
144
|
const spin = logger.spinner('Fetching your applications...').start();
|
|
96
145
|
|
|
@@ -111,20 +160,67 @@ async function linkExistingApplication() {
|
|
|
111
160
|
|
|
112
161
|
logger.newline();
|
|
113
162
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
163
|
+
let selectedApp = null;
|
|
164
|
+
|
|
165
|
+
// Try to auto-match based on Git remote URL
|
|
166
|
+
if (gitRemoteUrl) {
|
|
167
|
+
const normalizedRemote = normalizeGitUrl(gitRemoteUrl);
|
|
168
|
+
|
|
169
|
+
const matchedApp = applications.find(app => {
|
|
170
|
+
if (!app.git_repository) return false;
|
|
171
|
+
const normalizedAppRepo = normalizeGitUrl(app.git_repository);
|
|
172
|
+
return normalizedAppRepo === normalizedRemote;
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
if (matchedApp) {
|
|
176
|
+
// Found matching application!
|
|
177
|
+
logger.info(`Auto-detected Git repository: ${gitRemoteUrl}`);
|
|
178
|
+
logger.newline();
|
|
179
|
+
logger.field('Matched Application', matchedApp.name);
|
|
180
|
+
logger.field('Domain', matchedApp.domain || `${matchedApp.subdomain}.startanaicompany.com`);
|
|
181
|
+
logger.field('Status', matchedApp.status);
|
|
182
|
+
logger.newline();
|
|
183
|
+
|
|
184
|
+
const { confirm } = await inquirer.prompt([
|
|
185
|
+
{
|
|
186
|
+
type: 'confirm',
|
|
187
|
+
name: 'confirm',
|
|
188
|
+
message: 'Link this application to the current directory?',
|
|
189
|
+
default: true,
|
|
190
|
+
},
|
|
191
|
+
]);
|
|
192
|
+
|
|
193
|
+
if (confirm) {
|
|
194
|
+
selectedApp = matchedApp;
|
|
195
|
+
} else {
|
|
196
|
+
logger.newline();
|
|
197
|
+
logger.info('Please select a different application:');
|
|
198
|
+
logger.newline();
|
|
199
|
+
}
|
|
200
|
+
} else {
|
|
201
|
+
logger.warn(`No application found matching Git remote: ${gitRemoteUrl}`);
|
|
202
|
+
logger.newline();
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// If no auto-match or user declined, show interactive selection
|
|
207
|
+
if (!selectedApp) {
|
|
208
|
+
const choices = applications.map(app => ({
|
|
209
|
+
name: `${app.name} - ${app.domain || `${app.subdomain}.startanaicompany.com`} (${app.status})`,
|
|
210
|
+
value: app,
|
|
211
|
+
}));
|
|
212
|
+
|
|
213
|
+
const answer = await inquirer.prompt([
|
|
214
|
+
{
|
|
215
|
+
type: 'list',
|
|
216
|
+
name: 'selectedApp',
|
|
217
|
+
message: 'Select application to link to this directory:',
|
|
218
|
+
choices: choices,
|
|
219
|
+
},
|
|
220
|
+
]);
|
|
221
|
+
|
|
222
|
+
selectedApp = answer.selectedApp;
|
|
223
|
+
}
|
|
128
224
|
|
|
129
225
|
logger.newline();
|
|
130
226
|
|
package/src/commands/keys.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
const api = require('../lib/api');
|
|
6
|
-
const { getUser,
|
|
6
|
+
const { getUser, ensureAuthenticated } = require('../lib/config');
|
|
7
7
|
const logger = require('../lib/logger');
|
|
8
8
|
const inquirer = require('inquirer');
|
|
9
9
|
|
|
@@ -13,7 +13,7 @@ const inquirer = require('inquirer');
|
|
|
13
13
|
async function regenerate() {
|
|
14
14
|
try {
|
|
15
15
|
// Must be authenticated (via session token)
|
|
16
|
-
if (!
|
|
16
|
+
if (!(await ensureAuthenticated())) {
|
|
17
17
|
logger.error('Not logged in');
|
|
18
18
|
logger.newline();
|
|
19
19
|
logger.info('You must be logged in to regenerate your API key');
|
|
@@ -21,6 +21,10 @@ async function regenerate() {
|
|
|
21
21
|
logger.info('Login using email verification:');
|
|
22
22
|
logger.log(' saac login -e <email> # Request OTP');
|
|
23
23
|
logger.log(' saac login -e <email> --otp <code> # Verify OTP');
|
|
24
|
+
logger.newline();
|
|
25
|
+
logger.info('Or set environment variables:');
|
|
26
|
+
logger.log(' export SAAC_USER_API_KEY=your_api_key');
|
|
27
|
+
logger.log(' export SAAC_USER_EMAIL=your_email');
|
|
24
28
|
process.exit(1);
|
|
25
29
|
}
|
|
26
30
|
|
|
@@ -88,7 +92,7 @@ async function regenerate() {
|
|
|
88
92
|
*/
|
|
89
93
|
async function show() {
|
|
90
94
|
try {
|
|
91
|
-
if (!
|
|
95
|
+
if (!(await ensureAuthenticated())) {
|
|
92
96
|
logger.error('Not logged in');
|
|
93
97
|
logger.newline();
|
|
94
98
|
logger.info('Login first:');
|
package/src/commands/list.js
CHANGED
|
@@ -3,18 +3,17 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
const api = require('../lib/api');
|
|
6
|
-
const {
|
|
6
|
+
const { ensureAuthenticated } = require('../lib/config');
|
|
7
7
|
const logger = require('../lib/logger');
|
|
8
8
|
const { table } = require('table');
|
|
9
9
|
|
|
10
10
|
async function list() {
|
|
11
11
|
try {
|
|
12
|
-
// Check authentication
|
|
13
|
-
if (!
|
|
12
|
+
// Check authentication (with auto-login support)
|
|
13
|
+
if (!(await ensureAuthenticated())) {
|
|
14
14
|
logger.error('Not logged in');
|
|
15
|
-
logger.
|
|
16
|
-
logger.info('
|
|
17
|
-
logger.log(' saac login -e <email> -k <api-key>');
|
|
15
|
+
logger.info('Run: saac login -e <email> -k <api-key>');
|
|
16
|
+
logger.info('Or set: SAAC_USER_API_KEY and SAAC_USER_EMAIL');
|
|
18
17
|
process.exit(1);
|
|
19
18
|
}
|
|
20
19
|
|