@telelabsai/ship 1.1.11 → 1.1.14
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/sync.js +82 -105
- package/package.json +1 -1
package/cli/commands/sync.js
CHANGED
|
@@ -1,47 +1,29 @@
|
|
|
1
1
|
const https = require('https');
|
|
2
2
|
const http = require('http');
|
|
3
|
+
const { execSync, spawn } = require('child_process');
|
|
3
4
|
const fs = require('fs');
|
|
4
5
|
const path = require('path');
|
|
5
6
|
const prompts = require('prompts');
|
|
6
7
|
const { loadCredentials, DASHBOARD_URL } = require('./auth.js');
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
|
-
* ship sync [--workspace <slug>]
|
|
10
|
+
* ship sync [--workspace <slug>]
|
|
10
11
|
*
|
|
11
12
|
* Interactive mode (no flags): picks workspace with arrow keys
|
|
12
|
-
* CI mode (with
|
|
13
|
+
* CI mode (with --workspace flag): direct install
|
|
13
14
|
*/
|
|
14
15
|
function sync(args) {
|
|
15
|
-
const
|
|
16
|
-
const org = getArg(args, '--org') || getArg(args, '-o');
|
|
17
|
-
const key = getArg(args, '--key') || getArg(args, '-k');
|
|
18
|
-
const dir = getArg(args, '--dir') || process.cwd();
|
|
16
|
+
const workspaceSlug = getArg(args, '--workspace') || getArg(args, '-w');
|
|
19
17
|
|
|
20
18
|
// Resolve auth token
|
|
21
|
-
|
|
22
|
-
if (!token) {
|
|
23
|
-
const creds = loadCredentials();
|
|
24
|
-
if (creds?.token) token = creds.token;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
if (!token) {
|
|
19
|
+
const creds = loadCredentials();
|
|
20
|
+
if (!creds?.token) {
|
|
28
21
|
console.error(' Error: No authentication found.');
|
|
29
22
|
console.error(' Run: ship auth login\n');
|
|
30
23
|
process.exit(1);
|
|
31
24
|
}
|
|
32
25
|
|
|
33
|
-
|
|
34
|
-
if (org && workspace) {
|
|
35
|
-
doSync(org, workspace, token, dir);
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Interactive mode
|
|
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');
|
|
43
|
-
process.exit(1);
|
|
44
|
-
}
|
|
26
|
+
const token = creds.token;
|
|
45
27
|
|
|
46
28
|
console.log(' Fetching your workspaces...\n');
|
|
47
29
|
|
|
@@ -55,102 +37,97 @@ function sync(args) {
|
|
|
55
37
|
process.exit(1);
|
|
56
38
|
}
|
|
57
39
|
|
|
58
|
-
const
|
|
59
|
-
if (
|
|
60
|
-
console.log(' No
|
|
40
|
+
const workspaces = data.workspaces || [];
|
|
41
|
+
if (workspaces.length === 0) {
|
|
42
|
+
console.log(' No workspaces found. Join one at your Ship dashboard.\n');
|
|
61
43
|
process.exit(0);
|
|
62
44
|
}
|
|
63
45
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
46
|
+
let selected;
|
|
47
|
+
|
|
48
|
+
// If workspace slug provided → direct mode
|
|
49
|
+
if (workspaceSlug) {
|
|
50
|
+
selected = workspaces.find((w) => w.slug === workspaceSlug);
|
|
51
|
+
if (!selected) {
|
|
52
|
+
console.error(` Error: Workspace "${workspaceSlug}" not found.\n`);
|
|
53
|
+
console.log(' Available workspaces:');
|
|
54
|
+
workspaces.forEach((w) => console.log(` - ${w.slug} (${w.name})`));
|
|
55
|
+
console.log('');
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
} else {
|
|
59
|
+
// Interactive mode
|
|
60
|
+
if (!process.stdin.isTTY) {
|
|
61
|
+
console.error(' Error: --workspace required in non-interactive mode.');
|
|
62
|
+
console.error(' Usage: ship sync --workspace <slug>\n');
|
|
63
|
+
process.exit(1);
|
|
75
64
|
}
|
|
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
65
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
})
|
|
93
|
-
|
|
66
|
+
const { picked } = await prompts({
|
|
67
|
+
type: 'select',
|
|
68
|
+
name: 'picked',
|
|
69
|
+
message: 'Select workspace',
|
|
70
|
+
choices: workspaces.map((w) => ({
|
|
71
|
+
title: w.name,
|
|
72
|
+
description: w.description || '',
|
|
73
|
+
value: w,
|
|
74
|
+
})),
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
if (!picked) {
|
|
78
|
+
console.log(' Cancelled.\n');
|
|
79
|
+
process.exit(0);
|
|
80
|
+
}
|
|
94
81
|
|
|
95
|
-
|
|
96
|
-
console.log(' Cancelled.\n');
|
|
97
|
-
process.exit(0);
|
|
82
|
+
selected = picked;
|
|
98
83
|
}
|
|
99
84
|
|
|
100
85
|
// Confirmation
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
86
|
+
if (process.stdin.isTTY && !workspaceSlug) {
|
|
87
|
+
const { confirmed } = await prompts({
|
|
88
|
+
type: 'confirm',
|
|
89
|
+
name: 'confirmed',
|
|
90
|
+
message: `Install "${selected.name}" plugin into Claude Code?`,
|
|
91
|
+
initial: true,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
if (!confirmed) {
|
|
95
|
+
console.log(' Cancelled.\n');
|
|
96
|
+
process.exit(0);
|
|
97
|
+
}
|
|
111
98
|
}
|
|
112
99
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
function doSync(orgSlug, workspaceSlug, token, dir) {
|
|
119
|
-
console.log(` Syncing workspace "${workspaceSlug}"...\n`);
|
|
120
|
-
|
|
121
|
-
const url = `${DASHBOARD_URL}/api/sync/${orgSlug}?workspace=${workspaceSlug}`;
|
|
100
|
+
// Install via claude plugin add
|
|
101
|
+
console.log(`\n Installing "${selected.name}"...\n`);
|
|
122
102
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
console.error(` Error: ${data.error}\n`);
|
|
103
|
+
try {
|
|
104
|
+
// Check if claude is installed
|
|
105
|
+
execSync('claude --version', { stdio: 'pipe' });
|
|
106
|
+
} catch {
|
|
107
|
+
console.error(' Error: Claude Code is not installed.');
|
|
108
|
+
console.error(' Install it at: https://claude.ai/code\n');
|
|
130
109
|
process.exit(1);
|
|
131
110
|
}
|
|
132
111
|
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
if (file.path.endsWith('.gitkeep')) continue;
|
|
138
|
-
|
|
139
|
-
const filePath = path.join(claudeDir, file.path);
|
|
140
|
-
const fileDir = path.dirname(filePath);
|
|
112
|
+
const claude = spawn('claude', ['/plugin', 'marketplace', 'add', selected.deployUrl], {
|
|
113
|
+
stdio: 'inherit',
|
|
114
|
+
cwd: process.cwd(),
|
|
115
|
+
});
|
|
141
116
|
|
|
142
|
-
|
|
143
|
-
|
|
117
|
+
claude.on('close', (code) => {
|
|
118
|
+
if (code === 0) {
|
|
119
|
+
console.log(`\n ✓ "${selected.name}" installed successfully.\n`);
|
|
120
|
+
saveSyncConfig(process.cwd(), selected.slug);
|
|
121
|
+
} else {
|
|
122
|
+
console.error(`\n Plugin install exited with code ${code}\n`);
|
|
144
123
|
}
|
|
124
|
+
process.exit(code || 0);
|
|
125
|
+
});
|
|
145
126
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
console.log(`\n ✓ Synced ${written} files to ${claudeDir}\n`);
|
|
152
|
-
|
|
153
|
-
saveSyncConfig(dir, orgSlug, workspaceSlug);
|
|
127
|
+
claude.on('error', (err) => {
|
|
128
|
+
console.error(` Error running Claude Code: ${err.message}\n`);
|
|
129
|
+
process.exit(1);
|
|
130
|
+
});
|
|
154
131
|
});
|
|
155
132
|
}
|
|
156
133
|
|
|
@@ -182,9 +159,9 @@ function getArg(args, flag) {
|
|
|
182
159
|
return idx !== -1 && idx + 1 < args.length ? args[idx + 1] : null;
|
|
183
160
|
}
|
|
184
161
|
|
|
185
|
-
function saveSyncConfig(dir,
|
|
162
|
+
function saveSyncConfig(dir, workspaceSlug) {
|
|
186
163
|
const configFile = path.join(dir, '.ship.json');
|
|
187
|
-
const config = {
|
|
164
|
+
const config = { workspace: workspaceSlug, lastSynced: new Date().toISOString() };
|
|
188
165
|
fs.writeFileSync(configFile, JSON.stringify(config, null, 2), 'utf8');
|
|
189
166
|
}
|
|
190
167
|
|