slaunt 0.1.19 → 0.1.20
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.
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
name: Publish npm package
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v*'
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
id-token: write
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
publish:
|
|
14
|
+
runs-on: macos-14
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
- uses: actions/setup-node@v4
|
|
18
|
+
with:
|
|
19
|
+
node-version: '24'
|
|
20
|
+
package-manager-cache: false
|
|
21
|
+
- name: Install dependencies
|
|
22
|
+
run: npm ci --ignore-scripts
|
|
23
|
+
- name: Run tests
|
|
24
|
+
run: npm test
|
|
25
|
+
- name: Verify bundled helper
|
|
26
|
+
run: |
|
|
27
|
+
node -e "const f=require('fs'),c=require('crypto'),m=require('./release/manifest.json'); if(m.format!==1 || m.arch!=='arm64' || m.signing!=='adhoc' || !/^[a-f0-9]{40}$/.test(m.cdhash) || c.createHash('sha256').update(f.readFileSync('release/SlauntKillBar.zip')).digest('hex')!==m.sha256) process.exit(1)"
|
|
28
|
+
mkdir "$RUNNER_TEMP/slaunt-helper"
|
|
29
|
+
ditto -x -k release/SlauntKillBar.zip "$RUNNER_TEMP/slaunt-helper"
|
|
30
|
+
codesign --verify --deep --strict "$RUNNER_TEMP/slaunt-helper/SlauntKillBar.app"
|
|
31
|
+
EXPECTED_CDHASH="$(node -p "require('./release/manifest.json').cdhash")"
|
|
32
|
+
codesign -dv --verbose=4 "$RUNNER_TEMP/slaunt-helper/SlauntKillBar.app" 2> "$RUNNER_TEMP/codesign-info"
|
|
33
|
+
grep -Fx "CDHash=$EXPECTED_CDHASH" "$RUNNER_TEMP/codesign-info"
|
|
34
|
+
grep -Fx "Identifier=ai.slaunt.killbar" "$RUNNER_TEMP/codesign-info"
|
|
35
|
+
grep -Fx "Signature=adhoc" "$RUNNER_TEMP/codesign-info"
|
|
36
|
+
- name: Prepare public package metadata
|
|
37
|
+
env:
|
|
38
|
+
RELEASE_TAG: ${{ github.ref_name }}
|
|
39
|
+
run: |
|
|
40
|
+
node <<'NODE'
|
|
41
|
+
const fs = require('fs');
|
|
42
|
+
const tag = process.env.RELEASE_TAG;
|
|
43
|
+
if (!/^v(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)$/.test(tag)) throw Error('Expected a stable version tag, e.g. v0.1.20');
|
|
44
|
+
const p = JSON.parse(fs.readFileSync('package.json', 'utf8'));
|
|
45
|
+
p.name = 'slaunt';
|
|
46
|
+
p.version = tag.slice(1);
|
|
47
|
+
delete p.private;
|
|
48
|
+
p.bin = { slaunt: 'bin/slaunt-hooks.js', 'slaunt-hooks': 'bin/slaunt-hooks.js' };
|
|
49
|
+
p.repository = { type: 'git', url: 'git+https://github.com/kerodem/slaunt-hook-cli.git' };
|
|
50
|
+
fs.writeFileSync('package.json', JSON.stringify(p, null, 2) + '\n');
|
|
51
|
+
NODE
|
|
52
|
+
- name: Verify package contents
|
|
53
|
+
run: |
|
|
54
|
+
npm pack --dry-run --json > "$RUNNER_TEMP/slaunt-pack.json"
|
|
55
|
+
node -e "const p=require(process.env.RUNNER_TEMP+'/slaunt-pack.json')[0]; for(const n of ['bin/slaunt-hooks.js','release/manifest.json','release/SlauntKillBar.zip']) if(!p.files.some(f=>f.path===n)) throw Error('Missing package file: '+n)"
|
|
56
|
+
- name: Publish to npm
|
|
57
|
+
env:
|
|
58
|
+
NPM_CONFIG_PROVENANCE: 'false'
|
|
59
|
+
run: npm publish --access public
|
package/bin/slaunt-hooks.js
CHANGED
|
@@ -81,7 +81,8 @@ async function main(args=process.argv.slice(2),dependencies={}) {
|
|
|
81
81
|
}
|
|
82
82
|
else if(error.setupStage==='authorization') {
|
|
83
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.'};
|
|
84
|
-
|
|
84
|
+
const enrollmentMessages={'killbar-helper-unavailable':'KillBar helper is unavailable.','killbar-helper-untrusted':'KillBar helper could not be verified.','killbar-authentication-failed':'KillBar could not use the sign-in grant.','killbar-identity-invalid':'KillBar workspace identity is invalid.','killbar-lock-unavailable':'KillBar enrollment lock could not be created.','killbar-enrollment-busy':'Another KillBar enrollment is in progress.','killbar-status-unavailable':'KillBar status could not be read.','killbar-status-invalid':'KillBar returned an invalid status.','killbar-restart-required':'KillBar requires a restart before enrollment.','killbar-already-enrolled':'KillBar is already enrolled.','killbar-enrollment-failed':'KillBar enrollment request failed.','killbar-enrollment-unverified':'KillBar enrollment could not be verified for this workspace.'};
|
|
85
|
+
messages[error.message]=(error.setupDetail==='enrollment' && enrollmentMessages[error.enrollmentReason]) || details[error.setupDetail] || 'Slaunt authorization or enrollment could not be completed.';
|
|
85
86
|
}
|
|
86
87
|
else if(error.setupStage==='timeout') messages[error.message]='Slaunt authorization timed out.';
|
|
87
88
|
}
|
package/package.json
CHANGED
package/src/oauth-cohort.js
CHANGED
|
@@ -12,7 +12,7 @@ async function authorizeCohort({ clients, issuer, approve, login = runHiddenLogi
|
|
|
12
12
|
let timer;
|
|
13
13
|
let rejectOverall;
|
|
14
14
|
const failedPromise = new Promise((_, reject) => { rejectOverall = reject; });
|
|
15
|
-
const fail = (stage, client, detail) => {
|
|
15
|
+
const fail = (stage, client, detail, enrollmentReason) => {
|
|
16
16
|
if (failed) return;
|
|
17
17
|
failed = true;
|
|
18
18
|
clearTimeout(timer);
|
|
@@ -21,6 +21,8 @@ async function authorizeCohort({ clients, issuer, approve, login = runHiddenLogi
|
|
|
21
21
|
const error = new Error('cohort-failed');
|
|
22
22
|
error.setupStage = stage;
|
|
23
23
|
if (['sign-in','workspace','enrollment','client-grants'].includes(detail)) error.setupDetail = detail;
|
|
24
|
+
const enrollmentReasons=['killbar-helper-unavailable','killbar-helper-untrusted','killbar-authentication-failed','killbar-identity-invalid','killbar-lock-unavailable','killbar-enrollment-busy','killbar-status-unavailable','killbar-status-invalid','killbar-restart-required','killbar-already-enrolled','killbar-enrollment-failed','killbar-enrollment-unverified'];
|
|
25
|
+
if(stage==='authorization' && detail==='enrollment' && enrollmentReasons.includes(enrollmentReason)) error.enrollmentReason=enrollmentReason;
|
|
24
26
|
if (client === 'codex' || client === 'claude') error.setupClient = client;
|
|
25
27
|
if (stage === 'native-login' && ['launch','request','waiting-approval','callback','client-exit'].includes(detail)) error.nativeStage = detail;
|
|
26
28
|
rejectOverall(error);
|
|
@@ -36,7 +38,7 @@ async function authorizeCohort({ clients, issuer, approve, login = runHiddenLogi
|
|
|
36
38
|
if (failed) return;
|
|
37
39
|
if (!callbacks || typeof callbacks !== 'object' || Array.isArray(callbacks) || Object.keys(callbacks).length !== clients.length || clients.some(name => typeof callbacks[name] !== 'string')) throw new Error('cohort-invalid-response');
|
|
38
40
|
for (const name of clients) pending.get(name).resolve(callbacks[name]);
|
|
39
|
-
}).catch(error => fail('authorization', undefined, error?.setupDetail));
|
|
41
|
+
}).catch(error => fail('authorization', undefined, error?.setupDetail, error?.enrollmentReason));
|
|
40
42
|
});
|
|
41
43
|
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; }));
|
|
42
44
|
try { await Promise.race([Promise.all(runs), failedPromise]); } finally { clearTimeout(timer); }
|
package/src/setup-flow.js
CHANGED
|
@@ -59,7 +59,10 @@ async function runSetupFlow({clients,issuer=ISSUER,fetchImpl=fetch,shell={openEx
|
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
return payload.callbacks;
|
|
62
|
-
} catch { const error=new Error('setup-step-failed'); error.setupDetail=diagnosticStage;
|
|
62
|
+
} catch (cause) { const error=new Error('setup-step-failed'); error.setupDetail=diagnosticStage;
|
|
63
|
+
const allowed=['killbar-helper-unavailable','killbar-helper-untrusted','killbar-authentication-failed','killbar-identity-invalid','killbar-lock-unavailable','killbar-enrollment-busy','killbar-status-unavailable','killbar-status-invalid','killbar-restart-required','killbar-already-enrolled','killbar-enrollment-failed','killbar-enrollment-unverified'];
|
|
64
|
+
if(diagnosticStage==='enrollment' && allowed.includes(cause?.message)) error.enrollmentReason=cause.message;
|
|
65
|
+
throw error; }
|
|
63
66
|
}});
|
|
64
67
|
} finally {session=null;}
|
|
65
68
|
}
|
|
@@ -113,3 +113,9 @@ test('setup failures report only an allowlisted stage and never the underlying s
|
|
|
113
113
|
}),error=>error.message==='cohort-failed'&&error.setupDetail===stage&&!JSON.stringify(error).includes(secret));
|
|
114
114
|
}
|
|
115
115
|
});
|
|
116
|
+
|
|
117
|
+
test('enrollment diagnostics expose only allowed failure categories',async()=>{
|
|
118
|
+
for(const [message,expected] of [['killbar-status-unavailable','killbar-status-unavailable'],['killbar-enrollment-failed','killbar-enrollment-failed'],['token-secret-not-allowed',undefined]]){
|
|
119
|
+
await assert.rejects(runSetupFlow({clients:['codex'],issuer:'https://example.test',authenticate:async({sessionSink})=>sessionSink.save({accessToken:'private'}),killbarEnrollment:async()=>{throw new Error(message)},login:async({authorize})=>authorize({client:'codex',authorizationUrl:'request'}),fetchImpl:async()=>identityResponse()}),error=>error.setupDetail==='enrollment'&&error.enrollmentReason===expected&&!JSON.stringify(error).includes('token-secret-not-allowed'));
|
|
120
|
+
}
|
|
121
|
+
});
|