@aiam/ciba 0.8.0 → 0.8.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.
Files changed (2) hide show
  1. package/ciba.mjs +24 -6
  2. package/package.json +1 -1
package/ciba.mjs CHANGED
@@ -368,6 +368,19 @@ async function runAuthFlow(serverUrl, extraAttrs, opts) {
368
368
  log('');
369
369
  log(' Waiting for authentication...');
370
370
 
371
+ // For daemon mode we don't need to wait for the initial token — the daemon
372
+ // handles token delivery on demand via `ciba token`. Skip the blocking wait
373
+ // and go straight to starting the socket server after approval is confirmed.
374
+ // We detect approval by watching authResults indirectly: the server writes
375
+ // to the device doc's `meta.user_id` after approve (set in server.tsx).
376
+ if (opts.daemonMode) {
377
+ const meta2 = deviceDoc.getMap('meta');
378
+ await firstInYMap(meta2, (key) => key === 'user_id', TIMEOUT)
379
+ .catch((e) => { provider.destroy(); throw e; });
380
+ log(' ✓ Approved');
381
+ return { provider, deviceDoc, privateKey, plainToken: '', serverUrl, tokenMap: null };
382
+ }
383
+
371
384
  const resourcesMap = deviceDoc.getMap('resources');
372
385
  const tokenMapName = await firstInYMap(resourcesMap, () => true, TIMEOUT)
373
386
  .catch((e) => { provider.destroy(); throw e; });
@@ -629,9 +642,9 @@ const loginCmd = defineCommand({
629
642
  child.stderr.on('data', (d) => {
630
643
  output += d.toString();
631
644
  process.stderr.write(d);
632
- // Wait for Session readysocket is up and first token is cached.
633
- // The URL + user_code are printed before this so the user can approve.
634
- if (output.includes('Session ready')) {
645
+ // Detach as soon as url: is printed user has what they need to approve.
646
+ // The daemon keeps running in background waiting for approval then starts socket.
647
+ if (output.includes('url:') || output.includes('Session ready')) {
635
648
  child.unref();
636
649
  process.exit(0);
637
650
  }
@@ -643,7 +656,7 @@ const loginCmd = defineCommand({
643
656
  // --daemon: internal flag used by --persist fork
644
657
  const isDaemon = process.argv.includes('--daemon');
645
658
 
646
- const { provider, deviceDoc, privateKey, plainToken } = await runAuthFlow(serverUrl, extraAttrs, opts);
659
+ const { provider, deviceDoc, privateKey, plainToken } = await runAuthFlow(serverUrl, extraAttrs, { ...opts, daemonMode: isDaemon });
647
660
 
648
661
  if (isDaemon) {
649
662
  startDaemon(provider, deviceDoc, privateKey, serverUrl);
@@ -739,9 +752,14 @@ const logoutCmd = defineCommand({
739
752
  const stopCmd = defineCommand({
740
753
  meta: { description: 'Stop daemon and clear session' },
741
754
  args: {},
742
- run() {
743
- const cfg = loadConfig();
755
+ async run() {
744
756
  if (cfg.pid) { try { process.kill(parseInt(cfg.pid)); } catch {} }
757
+ // Kill all orphaned ciba daemon processes (previous --persist runs that
758
+ // were never approved or whose parent exited without storing the PID).
759
+ try {
760
+ const { execFileSync } = await import('node:child_process');
761
+ execFileSync('pkill', ['-f', 'ciba login --daemon'], { stdio: 'ignore' });
762
+ } catch { /* pkill not found or no matching processes */ }
745
763
  if (existsSync(SOCKET_PATH)) unlinkSync(SOCKET_PATH);
746
764
  const sessionFile = join(CONFIG_DIR, 'session');
747
765
  if (existsSync(sessionFile)) unlinkSync(sessionFile);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiam/ciba",
3
- "version": "0.8.0",
3
+ "version": "0.8.2",
4
4
  "description": "OAuth 2.0 Device Authorization Grant CLI with cross-device push approval (Yjs sync, ECDH-encrypted token delivery, persistent device id)",
5
5
  "type": "module",
6
6
  "bin": {