@telelabsai/ship 1.1.4 → 1.1.6
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/cli/commands/auth.js +1 -1
- package/cli/commands/sync.js +93 -44
- package/package.json +1 -1
package/cli/commands/auth.js
CHANGED
|
@@ -64,7 +64,7 @@ function login() {
|
|
|
64
64
|
|
|
65
65
|
server.listen(0, () => {
|
|
66
66
|
const port = server.address().port;
|
|
67
|
-
const authUrl = `${DASHBOARD_URL}/cli
|
|
67
|
+
const authUrl = `${DASHBOARD_URL}/authorize-cli?port=${port}`;
|
|
68
68
|
|
|
69
69
|
console.log(` Auth URL: ${authUrl}\n`);
|
|
70
70
|
console.log(' Waiting for authorization...\n');
|
package/cli/commands/sync.js
CHANGED
|
@@ -2,12 +2,14 @@ const https = require('https');
|
|
|
2
2
|
const http = require('http');
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
|
+
const readline = require('readline');
|
|
5
6
|
const { loadCredentials, DASHBOARD_URL } = require('./auth.js');
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
|
-
* ship sync --workspace <slug> [--org <slug>] [--key <key>] [--dir <path>]
|
|
9
|
+
* ship sync [--workspace <slug>] [--org <slug>] [--key <key>] [--dir <path>]
|
|
9
10
|
*
|
|
10
|
-
*
|
|
11
|
+
* Interactive mode (no flags): picks org + workspace from list
|
|
12
|
+
* CI mode (with flags): uses provided org + workspace directly
|
|
11
13
|
*/
|
|
12
14
|
function sync(args) {
|
|
13
15
|
const workspace = getArg(args, '--workspace') || getArg(args, '-w');
|
|
@@ -15,54 +17,113 @@ function sync(args) {
|
|
|
15
17
|
const key = getArg(args, '--key') || getArg(args, '-k');
|
|
16
18
|
const dir = getArg(args, '--dir') || process.cwd();
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
console.error(' Error: --workspace is required.');
|
|
20
|
-
console.error(' Usage: ship sync --workspace <slug>\n');
|
|
21
|
-
process.exit(1);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// Resolve auth token: --key flag > saved credentials
|
|
20
|
+
// Resolve auth token
|
|
25
21
|
let token = key;
|
|
26
22
|
if (!token) {
|
|
27
23
|
const creds = loadCredentials();
|
|
28
|
-
if (creds?.token)
|
|
29
|
-
token = creds.token;
|
|
30
|
-
}
|
|
24
|
+
if (creds?.token) token = creds.token;
|
|
31
25
|
}
|
|
32
26
|
|
|
33
27
|
if (!token) {
|
|
34
28
|
console.error(' Error: No authentication found.');
|
|
35
|
-
console.error(' Run: ship auth login');
|
|
36
|
-
|
|
29
|
+
console.error(' Run: ship auth login\n');
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// If both org and workspace provided → direct sync (CI mode)
|
|
34
|
+
if (org && workspace) {
|
|
35
|
+
doSync(org, workspace, token, dir);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Interactive mode — fetch orgs + workspaces, let user pick
|
|
40
|
+
if (!process.stdin.isTTY) {
|
|
41
|
+
console.error(' Error: --org and --workspace required in non-interactive mode.');
|
|
42
|
+
console.error(' Usage: ship sync --org <slug> --workspace <slug>\n');
|
|
37
43
|
process.exit(1);
|
|
38
44
|
}
|
|
39
45
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if (
|
|
48
|
-
|
|
49
|
-
} else {
|
|
50
|
-
console.error(' Error: --org is required (or run ship auth login and set default org).');
|
|
51
|
-
console.error(' Usage: ship sync --org <org-slug> --workspace <slug>\n');
|
|
46
|
+
console.log(' Fetching your workspaces...\n');
|
|
47
|
+
|
|
48
|
+
fetchJson(`${DASHBOARD_URL}/api/cli/workspaces`, token, (err, data) => {
|
|
49
|
+
if (err) {
|
|
50
|
+
console.error(` Error: ${err.message}\n`);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
if (data.error) {
|
|
54
|
+
console.error(` Error: ${data.error}\n`);
|
|
52
55
|
process.exit(1);
|
|
53
56
|
}
|
|
54
|
-
}
|
|
55
57
|
|
|
56
|
-
|
|
58
|
+
const orgs = data.orgs || [];
|
|
59
|
+
if (orgs.length === 0) {
|
|
60
|
+
console.log(' No organizations found. Create one at your Ship dashboard.\n');
|
|
61
|
+
process.exit(0);
|
|
62
|
+
}
|
|
57
63
|
|
|
58
|
-
|
|
64
|
+
// Flatten all workspaces with org info
|
|
65
|
+
const allWorkspaces = [];
|
|
66
|
+
for (const o of orgs) {
|
|
67
|
+
for (const w of o.workspaces) {
|
|
68
|
+
allWorkspaces.push({
|
|
69
|
+
orgSlug: o.slug,
|
|
70
|
+
orgName: o.name,
|
|
71
|
+
wsSlug: w.slug,
|
|
72
|
+
wsName: w.name,
|
|
73
|
+
files: w.files,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (allWorkspaces.length === 0) {
|
|
79
|
+
console.log(' No workspaces found. Create one at your Ship dashboard.\n');
|
|
80
|
+
process.exit(0);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Show picker
|
|
84
|
+
console.log(' Available workspaces:\n');
|
|
85
|
+
allWorkspaces.forEach((w, i) => {
|
|
86
|
+
console.log(` ${i + 1}. ${w.wsName} (${w.orgName}) — ${w.files} files`);
|
|
87
|
+
});
|
|
88
|
+
console.log('');
|
|
89
|
+
|
|
90
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
91
|
+
|
|
92
|
+
rl.question(' Select workspace [1]: ', (answer) => {
|
|
93
|
+
const idx = parseInt(answer || '1', 10) - 1;
|
|
94
|
+
if (idx < 0 || idx >= allWorkspaces.length) {
|
|
95
|
+
console.error(' Invalid selection.\n');
|
|
96
|
+
rl.close();
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const selected = allWorkspaces[idx];
|
|
101
|
+
console.log(`\n Will sync "${selected.wsName}" (${selected.files} files) to ${path.join(dir, '.claude')}`);
|
|
102
|
+
|
|
103
|
+
rl.question(' Proceed? [Y/n]: ', (confirm) => {
|
|
104
|
+
rl.close();
|
|
105
|
+
const c = (confirm || 'y').toLowerCase().trim();
|
|
106
|
+
if (c !== 'y' && c !== 'yes' && c !== '') {
|
|
107
|
+
console.log(' Cancelled.\n');
|
|
108
|
+
process.exit(0);
|
|
109
|
+
}
|
|
110
|
+
console.log('');
|
|
111
|
+
doSync(selected.orgSlug, selected.wsSlug, token, dir);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function doSync(orgSlug, workspaceSlug, token, dir) {
|
|
118
|
+
console.log(` Syncing workspace "${workspaceSlug}" from org "${orgSlug}"...\n`);
|
|
119
|
+
|
|
120
|
+
const url = `${DASHBOARD_URL}/api/sync/${orgSlug}?workspace=${workspaceSlug}`;
|
|
59
121
|
|
|
60
122
|
fetchJson(url, token, (err, data) => {
|
|
61
123
|
if (err) {
|
|
62
124
|
console.error(` Error: ${err.message}\n`);
|
|
63
125
|
process.exit(1);
|
|
64
126
|
}
|
|
65
|
-
|
|
66
127
|
if (data.error) {
|
|
67
128
|
console.error(` Error: ${data.error}\n`);
|
|
68
129
|
process.exit(1);
|
|
@@ -72,7 +133,6 @@ function sync(args) {
|
|
|
72
133
|
let written = 0;
|
|
73
134
|
|
|
74
135
|
for (const file of data.files || []) {
|
|
75
|
-
// Skip .gitkeep files
|
|
76
136
|
if (file.path.endsWith('.gitkeep')) continue;
|
|
77
137
|
|
|
78
138
|
const filePath = path.join(claudeDir, file.path);
|
|
@@ -89,8 +149,8 @@ function sync(args) {
|
|
|
89
149
|
|
|
90
150
|
console.log(`\n Synced ${written} files to ${claudeDir}\n`);
|
|
91
151
|
|
|
92
|
-
// Save
|
|
93
|
-
saveSyncConfig(dir, orgSlug,
|
|
152
|
+
// Save config for future syncs
|
|
153
|
+
saveSyncConfig(dir, orgSlug, workspaceSlug);
|
|
94
154
|
});
|
|
95
155
|
}
|
|
96
156
|
|
|
@@ -128,15 +188,4 @@ function saveSyncConfig(dir, orgSlug, workspace) {
|
|
|
128
188
|
fs.writeFileSync(configFile, JSON.stringify(config, null, 2), 'utf8');
|
|
129
189
|
}
|
|
130
190
|
|
|
131
|
-
function loadSavedOrg(dir) {
|
|
132
|
-
try {
|
|
133
|
-
const configFile = path.join(dir, '.ship.json');
|
|
134
|
-
if (fs.existsSync(configFile)) {
|
|
135
|
-
const config = JSON.parse(fs.readFileSync(configFile, 'utf8'));
|
|
136
|
-
return config.org || null;
|
|
137
|
-
}
|
|
138
|
-
} catch {}
|
|
139
|
-
return null;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
191
|
module.exports = sync;
|