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
@@ -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
- messages[error.message]=details[error.setupDetail] || 'Slaunt authorization or enrollment could not be completed.';
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "slaunt",
3
- "version": "0.1.19",
3
+ "version": "0.1.20",
4
4
  "bin": {
5
5
  "slaunt": "bin/slaunt-hooks.js",
6
6
  "slaunt-hooks": "bin/slaunt-hooks.js"
@@ -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; throw error; }
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
+ });