@somewhere-tech/cli 0.2.0 → 0.2.2
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/dist/commands/auth.js +129 -20
- package/dist/commands/auth.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/lib/device-login.js +80 -0
- package/dist/lib/device-login.js.map +1 -0
- package/package.json +1 -1
package/dist/commands/auth.js
CHANGED
|
@@ -1,30 +1,21 @@
|
|
|
1
1
|
import ora from 'ora';
|
|
2
2
|
import { browserLogin } from '../lib/auth.js';
|
|
3
|
+
import { deviceLogin, DeviceLoginTimeout, DeviceCodeUnsupported, } from '../lib/device-login.js';
|
|
3
4
|
import { ApiClient } from '../lib/client.js';
|
|
4
|
-
import { clearConfig, loadConfig, saveConfig, saveGlobalMcpConfig } from '../lib/config.js';
|
|
5
|
+
import { clearConfig, loadConfig, saveConfig, saveGlobalMcpConfig, } from '../lib/config.js';
|
|
6
|
+
import { getDeviceId, getDeviceKeyName } from '../lib/device.js';
|
|
5
7
|
import { dim, error, info, success, teal } from '../lib/output.js';
|
|
6
8
|
export function registerAuth(program) {
|
|
7
9
|
program
|
|
8
10
|
.command('login')
|
|
9
|
-
.description('Authenticate with somewhere.tech
|
|
10
|
-
.
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
saveGlobalMcpConfig(config.token);
|
|
16
|
-
spinner.stop();
|
|
17
|
-
success(`Logged in as ${teal(config.user.email)}`);
|
|
18
|
-
console.log('');
|
|
19
|
-
info('Done. Claude Code is connected.');
|
|
20
|
-
info('Start a new session to use Somewhere tools.');
|
|
21
|
-
process.exit(0);
|
|
22
|
-
}
|
|
23
|
-
catch (err) {
|
|
24
|
-
spinner.fail('Login failed');
|
|
25
|
-
error(err instanceof Error ? err.message : String(err));
|
|
26
|
-
process.exit(1);
|
|
11
|
+
.description('Authenticate with somewhere.tech')
|
|
12
|
+
.option('--legacy', 'Use the localhost-callback flow instead of device code')
|
|
13
|
+
.action(async (opts) => {
|
|
14
|
+
if (opts.legacy) {
|
|
15
|
+
await runLegacyLogin();
|
|
16
|
+
return;
|
|
27
17
|
}
|
|
18
|
+
await runDeviceLogin();
|
|
28
19
|
});
|
|
29
20
|
program
|
|
30
21
|
.command('logout')
|
|
@@ -53,10 +44,128 @@ export function registerAuth(program) {
|
|
|
53
44
|
info(`Projects: ${projects.projects.length} (${deployed} deployed, ${draft} draft)`);
|
|
54
45
|
info(`Profile: ${config.user.username}.somewhere.tech`);
|
|
55
46
|
}
|
|
56
|
-
catch
|
|
47
|
+
catch {
|
|
57
48
|
console.log(teal(config.user.email));
|
|
58
49
|
info(dim('Could not fetch account details'));
|
|
59
50
|
}
|
|
60
51
|
});
|
|
52
|
+
const auth = program.command('auth').description('Manage stored credentials');
|
|
53
|
+
auth
|
|
54
|
+
.command('set <token>')
|
|
55
|
+
.description('Save an smt_ token directly (no browser flow)')
|
|
56
|
+
.action(async (token) => {
|
|
57
|
+
if (!token.startsWith('smt_')) {
|
|
58
|
+
error('Token must start with smt_');
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
// Verify the token works before persisting it.
|
|
62
|
+
const client = new ApiClient(token);
|
|
63
|
+
let email = '';
|
|
64
|
+
let username = '';
|
|
65
|
+
try {
|
|
66
|
+
const me = await client.call('GET', '/auth/platform-me');
|
|
67
|
+
email = me.email ?? '';
|
|
68
|
+
username = me.username ?? '';
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
error(`Token is not valid: ${err instanceof Error ? err.message : String(err)}`);
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
saveConfig({ token, user: { email, username } });
|
|
75
|
+
saveGlobalMcpConfig(token);
|
|
76
|
+
success(`Token saved.${email ? ` Logged in as ${teal(email)}.` : ''}`);
|
|
77
|
+
info('Run `somewhere projects` to verify.');
|
|
78
|
+
});
|
|
79
|
+
auth
|
|
80
|
+
.command('status')
|
|
81
|
+
.description('Show current login state, device ID, and key name')
|
|
82
|
+
.action(() => {
|
|
83
|
+
const config = loadConfig();
|
|
84
|
+
const deviceId = getDeviceId();
|
|
85
|
+
const keyName = getDeviceKeyName();
|
|
86
|
+
if (!config) {
|
|
87
|
+
info('Not logged in.');
|
|
88
|
+
info(`Device ID: ${deviceId}`);
|
|
89
|
+
info(`Key name: ${keyName}`);
|
|
90
|
+
info(dim('Run `somewhere login` to authenticate.'));
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
success(`Logged in as ${teal(config.user.email || '(unknown)')}`);
|
|
94
|
+
info(`Device ID: ${deviceId}`);
|
|
95
|
+
info(`Key name: ${keyName}`);
|
|
96
|
+
info(`Token: ${config.token.slice(0, 8)}…${config.token.slice(-4)}`);
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
function installCancelHandler(getSpinner) {
|
|
100
|
+
process.on('SIGINT', () => {
|
|
101
|
+
getSpinner()?.stop();
|
|
102
|
+
process.stdout.write('\u001B[?25h'); // restore cursor in case ora hid it
|
|
103
|
+
console.log('');
|
|
104
|
+
info('Login cancelled.');
|
|
105
|
+
process.exit(130);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
async function runDeviceLogin() {
|
|
109
|
+
console.log('');
|
|
110
|
+
console.log(' Login to somewhere.tech');
|
|
111
|
+
console.log('');
|
|
112
|
+
let spinner = null;
|
|
113
|
+
installCancelHandler(() => spinner);
|
|
114
|
+
try {
|
|
115
|
+
const config = await deviceLogin({
|
|
116
|
+
onPrompt: ({ code, approvalUrl }) => {
|
|
117
|
+
console.log(' Open this URL in any browser:');
|
|
118
|
+
console.log(` ${teal(approvalUrl)}`);
|
|
119
|
+
console.log('');
|
|
120
|
+
console.log(` Enter code: ${teal(code)}`);
|
|
121
|
+
console.log('');
|
|
122
|
+
},
|
|
123
|
+
onWaiting: () => {
|
|
124
|
+
spinner = ora({
|
|
125
|
+
text: 'Waiting for approval… (press Ctrl+C to cancel)',
|
|
126
|
+
color: 'cyan',
|
|
127
|
+
}).start();
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
spinner?.stop();
|
|
131
|
+
saveConfig(config);
|
|
132
|
+
saveGlobalMcpConfig(config.token);
|
|
133
|
+
success(`Logged in as ${teal(config.user.email || '(unknown)')}`);
|
|
134
|
+
success(`Device: ${getDeviceKeyName()}`);
|
|
135
|
+
success('Claude Code MCP configured');
|
|
136
|
+
process.exit(0);
|
|
137
|
+
}
|
|
138
|
+
catch (err) {
|
|
139
|
+
spinner?.stop();
|
|
140
|
+
if (err instanceof DeviceLoginTimeout) {
|
|
141
|
+
error(err.message);
|
|
142
|
+
process.exit(1);
|
|
143
|
+
}
|
|
144
|
+
if (err instanceof DeviceCodeUnsupported) {
|
|
145
|
+
info(dim('Falling back to localhost-callback login…'));
|
|
146
|
+
await runLegacyLogin();
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
error(err instanceof Error ? err.message : String(err));
|
|
150
|
+
process.exit(1);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
async function runLegacyLogin() {
|
|
154
|
+
const spinner = ora('Waiting for browser login… (press Ctrl+C to cancel)').start();
|
|
155
|
+
installCancelHandler(() => spinner);
|
|
156
|
+
try {
|
|
157
|
+
const config = await browserLogin();
|
|
158
|
+
saveConfig(config);
|
|
159
|
+
saveGlobalMcpConfig(config.token);
|
|
160
|
+
spinner.stop();
|
|
161
|
+
success(`Logged in as ${teal(config.user.email)}`);
|
|
162
|
+
success('Claude Code MCP configured');
|
|
163
|
+
process.exit(0);
|
|
164
|
+
}
|
|
165
|
+
catch (err) {
|
|
166
|
+
spinner.fail('Login failed');
|
|
167
|
+
error(err instanceof Error ? err.message : String(err));
|
|
168
|
+
process.exit(1);
|
|
169
|
+
}
|
|
61
170
|
}
|
|
62
171
|
//# sourceMappingURL=auth.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -13,7 +13,7 @@ import { registerMcp } from './commands/mcp.js';
|
|
|
13
13
|
const program = new Command()
|
|
14
14
|
.name('somewhere')
|
|
15
15
|
.description('CLI for somewhere.tech')
|
|
16
|
-
.version('0.2.
|
|
16
|
+
.version('0.2.1');
|
|
17
17
|
registerAuth(program);
|
|
18
18
|
registerInit(program);
|
|
19
19
|
registerProject(program);
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Device-code login. Same pattern as `npm login` and `gh auth login`.
|
|
3
|
+
*
|
|
4
|
+
* 1. Generate a human-readable XXXX-XXXX code
|
|
5
|
+
* 2. POST it to the platform — server stores it pending
|
|
6
|
+
* 3. Print the URL + code, poll status
|
|
7
|
+
* 4. When the user approves in a browser, the next poll returns the token
|
|
8
|
+
*
|
|
9
|
+
* Why over the localhost-callback flow:
|
|
10
|
+
* - works over SSH / cloud dev environments
|
|
11
|
+
* - no port binding, no firewall holes
|
|
12
|
+
* - browser and terminal can be on different machines
|
|
13
|
+
*/
|
|
14
|
+
import { randomInt } from 'node:crypto';
|
|
15
|
+
import { getDeviceKeyName } from './device.js';
|
|
16
|
+
const API_BASE = 'https://api.somewhere.tech';
|
|
17
|
+
const POLL_INTERVAL_MS = 2000;
|
|
18
|
+
const TIMEOUT_MS = 5 * 60 * 1000;
|
|
19
|
+
// Skip ambiguous chars (no I, O, 0, 1) — same alphabet the worker validates.
|
|
20
|
+
const ALPHABET = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
|
|
21
|
+
function generateCode() {
|
|
22
|
+
const chars = [];
|
|
23
|
+
for (let i = 0; i < 8; i++) {
|
|
24
|
+
chars.push(ALPHABET[randomInt(0, ALPHABET.length)]);
|
|
25
|
+
}
|
|
26
|
+
return `${chars.slice(0, 4).join('')}-${chars.slice(4).join('')}`;
|
|
27
|
+
}
|
|
28
|
+
export class DeviceLoginTimeout extends Error {
|
|
29
|
+
constructor() {
|
|
30
|
+
super('Login timed out. Run `somewhere login` to try again.');
|
|
31
|
+
this.name = 'DeviceLoginTimeout';
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export class DeviceCodeUnsupported extends Error {
|
|
35
|
+
constructor() {
|
|
36
|
+
super('Device-code endpoint not available on this platform.');
|
|
37
|
+
this.name = 'DeviceCodeUnsupported';
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export async function deviceLogin(callbacks) {
|
|
41
|
+
const code = generateCode();
|
|
42
|
+
const deviceName = getDeviceKeyName();
|
|
43
|
+
const startRes = await fetch(`${API_BASE}/v1/auth/device-code`, {
|
|
44
|
+
method: 'POST',
|
|
45
|
+
headers: { 'Content-Type': 'application/json' },
|
|
46
|
+
body: JSON.stringify({ code, device_name: deviceName }),
|
|
47
|
+
});
|
|
48
|
+
if (startRes.status === 404) {
|
|
49
|
+
// Endpoint not deployed yet → caller can fall back to legacy flow
|
|
50
|
+
throw new DeviceCodeUnsupported();
|
|
51
|
+
}
|
|
52
|
+
if (!startRes.ok) {
|
|
53
|
+
const text = await startRes.text();
|
|
54
|
+
throw new Error(`Failed to start login: HTTP ${startRes.status} ${text.slice(0, 200)}`);
|
|
55
|
+
}
|
|
56
|
+
const start = (await startRes.json());
|
|
57
|
+
callbacks.onPrompt({ code, approvalUrl: start.data.approval_url });
|
|
58
|
+
callbacks.onWaiting?.();
|
|
59
|
+
const startedAt = Date.now();
|
|
60
|
+
while (Date.now() - startedAt < TIMEOUT_MS) {
|
|
61
|
+
await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
|
|
62
|
+
const pollRes = await fetch(`${API_BASE}/v1/auth/device-code/status?code=${encodeURIComponent(code)}`);
|
|
63
|
+
if (!pollRes.ok) {
|
|
64
|
+
// Transient errors are non-fatal — keep polling until timeout.
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
const status = (await pollRes.json());
|
|
68
|
+
if (status.data.status === 'expired') {
|
|
69
|
+
throw new DeviceLoginTimeout();
|
|
70
|
+
}
|
|
71
|
+
if (status.data.status === 'approved') {
|
|
72
|
+
return {
|
|
73
|
+
token: status.data.token,
|
|
74
|
+
user: { email: status.data.email, username: '' },
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
throw new DeviceLoginTimeout();
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=device-login.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-login.js","sourceRoot":"","sources":["../../src/lib/device-login.ts"],"names":[],"mappings":"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"}
|