slaunt 0.1.18 → 0.1.19
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/slaunt-hooks.js +4 -1
- package/package.json +1 -1
- package/src/full-screen.js +1 -3
- package/src/hidden-oauth.js +1 -1
- package/src/oauth-cohort.js +2 -1
- package/test/hidden-oauth.test.js +12 -0
package/bin/slaunt-hooks.js
CHANGED
|
@@ -75,7 +75,10 @@ async function main(args=process.argv.slice(2),dependencies={}) {
|
|
|
75
75
|
if(clientLabel && error?.message==='client-configuration-conflict') messages[error.message]=`${clientLabel} already has a conflicting Slaunt configuration.`;
|
|
76
76
|
if(clientLabel && error?.message==='client-configuration-failed') messages[error.message]=`${clientLabel} configuration could not be updated.`;
|
|
77
77
|
if(error?.message==='cohort-failed') {
|
|
78
|
-
if(error.setupStage==='native-login' && clientLabel)
|
|
78
|
+
if(error.setupStage==='native-login' && clientLabel) {
|
|
79
|
+
const nativeMessages={launch:'could not be started',request:'did not provide a valid authorization request', 'waiting-approval':'exited while waiting for authorization', callback:'could not complete the authorization handoff', 'client-exit':'did not complete authorization after the callback'};
|
|
80
|
+
messages[error.message]=`${clientLabel} ${nativeMessages[error.nativeStage] || 'could not complete its Slaunt authorization'}.`;
|
|
81
|
+
}
|
|
79
82
|
else if(error.setupStage==='authorization') {
|
|
80
83
|
const details={'sign-in':'Slaunt sign-in could not be completed.','workspace':'Slaunt workspace identity could not be verified.','enrollment':'KillBar enrollment could not be completed.','client-grants':'Slaunt could not issue client authorization grants.'};
|
|
81
84
|
messages[error.message]=details[error.setupDetail] || 'Slaunt authorization or enrollment could not be completed.';
|
package/package.json
CHANGED
package/src/full-screen.js
CHANGED
|
@@ -21,7 +21,7 @@ function createFullScreen({output,logo,color,motion}) {
|
|
|
21
21
|
const contents=[];
|
|
22
22
|
const add=(text='',tone='normal')=>contents.push({text,tone});
|
|
23
23
|
add('');
|
|
24
|
-
add(view==='select'?' CONNECT YOUR CLIENTS':'
|
|
24
|
+
add(view==='select'?' CONNECT YOUR CLIENTS':' Installing Slaunt','heading');
|
|
25
25
|
add('');
|
|
26
26
|
if(view==='select'){
|
|
27
27
|
add(cols<52?' Choose clients':' Which clients would you like to connect?','title');
|
|
@@ -42,8 +42,6 @@ function createFullScreen({output,logo,color,motion}) {
|
|
|
42
42
|
if(spacious)add('');
|
|
43
43
|
add(' ↑↓ move ↵ select esc','muted');
|
|
44
44
|
} else {
|
|
45
|
-
add(` ${selection||'Preparing your setup'}`,'title');
|
|
46
|
-
add('');
|
|
47
45
|
for(const item of completed.slice(-4))add(` ✓ ${item}`,'success');
|
|
48
46
|
if(current)add(` ${motion?'⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏'[tick%10]:'…'} ${current}`,'selected');
|
|
49
47
|
if(failure){add(` ! ${failure.message}`,'warning');if(failure.detail)add(` ${failure.detail}`,'muted');}
|
package/src/hidden-oauth.js
CHANGED
|
@@ -34,7 +34,7 @@ async function runHiddenLogin({ executable, commandPath = executable, issuer, au
|
|
|
34
34
|
let settled = false, text = '', found = false, submitted = false, timer;
|
|
35
35
|
const result = new Promise((resolve, reject) => {
|
|
36
36
|
const onAbort = () => finish(new Error('login-cancelled'));
|
|
37
|
-
const finish = (err) => { if (settled) return; settled = true; clearTimeout(timer); signal?.removeEventListener('abort', onAbort); if (err) { child.kill(); reject(err); } else resolve(); };
|
|
37
|
+
const finish = (err) => { if (err) { err.nativeStage = err.message === 'client-start-failed' ? 'launch' : err.message === 'authorization-handoff-failed' || err.message === 'login-input-failed' ? 'callback' : submitted ? 'client-exit' : found ? 'waiting-approval' : 'request'; } if (settled) return; settled = true; clearTimeout(timer); signal?.removeEventListener('abort', onAbort); if (err) { child.kill(); reject(err); } else resolve(); };
|
|
38
38
|
if (signal?.aborted) { finish(new Error('login-cancelled')); return; }
|
|
39
39
|
signal?.addEventListener('abort', onAbort, { once: true });
|
|
40
40
|
child.stdin.on('error', () => finish(new Error('login-input-failed')));
|
package/src/oauth-cohort.js
CHANGED
|
@@ -22,6 +22,7 @@ async function authorizeCohort({ clients, issuer, approve, login = runHiddenLogi
|
|
|
22
22
|
error.setupStage = stage;
|
|
23
23
|
if (['sign-in','workspace','enrollment','client-grants'].includes(detail)) error.setupDetail = detail;
|
|
24
24
|
if (client === 'codex' || client === 'claude') error.setupClient = client;
|
|
25
|
+
if (stage === 'native-login' && ['launch','request','waiting-approval','callback','client-exit'].includes(detail)) error.nativeStage = detail;
|
|
25
26
|
rejectOverall(error);
|
|
26
27
|
};
|
|
27
28
|
timer = setTimeout(() => fail('timeout'), timeoutMs);
|
|
@@ -37,7 +38,7 @@ async function authorizeCohort({ clients, issuer, approve, login = runHiddenLogi
|
|
|
37
38
|
for (const name of clients) pending.get(name).resolve(callbacks[name]);
|
|
38
39
|
}).catch(error => fail('authorization', undefined, error?.setupDetail));
|
|
39
40
|
});
|
|
40
|
-
const runs = clients.map(executable => Promise.resolve().then(() => login({executable,commandPath:executablePaths[executable] || executable,issuer,authorize,timeoutMs,signal:controller.signal})).catch(error => { fail('native-login', executable); throw error; }));
|
|
41
|
+
const runs = clients.map(executable => Promise.resolve().then(() => login({executable,commandPath:executablePaths[executable] || executable,issuer,authorize,timeoutMs,signal:controller.signal})).catch(error => { fail('native-login', executable, error?.nativeStage); throw error; }));
|
|
41
42
|
try { await Promise.race([Promise.all(runs), failedPromise]); } finally { clearTimeout(timer); }
|
|
42
43
|
}
|
|
43
44
|
module.exports = { authorizeCohort };
|
|
@@ -42,3 +42,15 @@ test('does not report success when client exits before callback is supplied', as
|
|
|
42
42
|
child.stdout.write(request + '\n'); child.emit('close',0);
|
|
43
43
|
await assert.rejects(running,/login-failed/);
|
|
44
44
|
});
|
|
45
|
+
|
|
46
|
+
test('reports bounded native handoff stages without child output', async () => {
|
|
47
|
+
const create = () => { const c = new EventEmitter(); c.stdin = new PassThrough(); c.stdout = new PassThrough(); c.stderr = new PassThrough(); c.kill = () => {}; return c; };
|
|
48
|
+
for (const phase of ['launch','request','waiting-approval','callback','client-exit']) {
|
|
49
|
+
const child = create();
|
|
50
|
+
const running = runHiddenLogin({executable:'claude',issuer,spawnImpl:()=>child,authorize:async()=>phase==='callback' ? 'http://bad.example/secret' : phase==='waiting-approval' ? new Promise(()=>{}) : 'http://127.0.0.1:1234/callback?code=ok&state=xyz'});
|
|
51
|
+
if (phase === 'launch') child.emit('error',new Error('secret-child-output'));
|
|
52
|
+
else if (phase === 'request') { child.stderr.write('secret-child-output\n'); child.emit('close',1); }
|
|
53
|
+
else { child.stdout.write(request+'\n'); if (phase==='waiting-approval') child.emit('close',1); if (phase==='client-exit') child.stdin.on('end',()=>child.emit('close',1)); if (phase==='client-exit') child.stdin.resume(); }
|
|
54
|
+
await assert.rejects(running,e=>e.nativeStage===phase && !JSON.stringify(e).includes('secret'));
|
|
55
|
+
}
|
|
56
|
+
});
|