@aiam/ciba 0.9.4 → 0.9.6

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 (3) hide show
  1. package/ciba.mjs +30 -0
  2. package/package.json +1 -1
  3. package/token.mjs +29 -6
package/ciba.mjs CHANGED
@@ -689,6 +689,35 @@ const refreshCmd = defineCommand({
689
689
  },
690
690
  });
691
691
 
692
+ const inspectCmd = defineCommand({
693
+ meta: { description: 'Open live device doc inspector in browser' },
694
+ args: {
695
+ url: { ...serverArg },
696
+ },
697
+ async run({ args }) {
698
+ const serverUrl = args.url || process.env.CIBA_URL || loadConfig().url || DEFAULT_SERVER_URL;
699
+ const keys = loadOrGenerateEcdhKeys();
700
+ const { jwt: deviceJwt } = signDeviceJwt(keys.privateKey, keys.publicKey);
701
+
702
+ // POST /inspect with device JWT to get a one-time inspect code.
703
+ const res = await fetch(`${serverUrl}/inspect`, {
704
+ method: 'POST',
705
+ headers: { Authorization: `Bearer ${deviceJwt}` },
706
+ });
707
+ if (!res.ok) {
708
+ const err = await res.json().catch(() => ({}));
709
+ process.stderr.write(`Error: ${err.error || res.status}\n`);
710
+ process.exit(1);
711
+ }
712
+ const { code, url } = await res.json();
713
+ process.stderr.write(`→ inspect code: ${code}\n`);
714
+ process.stderr.write(`→ opening: ${url}\n`);
715
+ const cmd = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start ""' : 'xdg-open';
716
+ exec(`${cmd} "${url}"`);
717
+ process.exit(0);
718
+ },
719
+ });
720
+
692
721
  const logoutCmd = defineCommand({
693
722
  meta: { description: 'Clear device keys, session and config (next login generates a new device identity)' },
694
723
  args: {},
@@ -764,6 +793,7 @@ const main = defineCommand({
764
793
  login: loginCmd,
765
794
  token: tokenCmd,
766
795
  refresh: refreshCmd,
796
+ inspect: inspectCmd,
767
797
  stop: stopCmd,
768
798
  status: statusCmd,
769
799
  logout: logoutCmd,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiam/ciba",
3
- "version": "0.9.4",
3
+ "version": "0.9.6",
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": {
package/token.mjs CHANGED
@@ -30,11 +30,22 @@ const args = process.argv.slice(2);
30
30
  const get = (flag) => { const i = args.indexOf(flag); return i !== -1 ? args[i + 1] : undefined; };
31
31
  const has = (flag) => args.includes(flag);
32
32
 
33
- const resource = get('--resource') ?? `urn:sap:destination:${process.env.CIBA_DEFAULT_DESTINATION || 'WEBAGENTS_BACKEND'}`;
34
- const jsonOut = has('--json');
35
- const envOut = has('--env');
33
+ const resource = get('--resource') ?? `urn:sap:destination:${process.env.CIBA_DEFAULT_DESTINATION || 'WEBAGENTS_BACKEND'}`;
34
+ const jsonOut = has('--json');
35
+ const envOut = has('--env');
36
36
  const isRefresh = has('--refresh');
37
37
 
38
+ // Collect all unrecognised --key value pairs as extra request attrs.
39
+ // Server uses them for login-hint, tenant, scope, app_tid, etc.
40
+ const KNOWN = new Set(['--resource', '--url', '--json', '--env', '--refresh']);
41
+ const extraAttrs = {};
42
+ for (let i = 0; i < args.length; i++) {
43
+ if (args[i].startsWith('--') && !KNOWN.has(args[i]) && i + 1 < args.length && !args[i + 1].startsWith('--')) {
44
+ extraAttrs[args[i].slice(2).replace(/-/g, '_')] = args[i + 1];
45
+ i++;
46
+ }
47
+ }
48
+
38
49
  // ─── Config ───────────────────────────────────────────────────────────────────
39
50
 
40
51
  function loadConfig() {
@@ -119,7 +130,17 @@ function tryGet(resourcesMap, deviceDoc, res) {
119
130
 
120
131
  // ─── Output ───────────────────────────────────────────────────────────────────
121
132
 
122
- function log(msg) { process.stderr.write(msg + '\n'); }
133
+ // Log entries go to Yjs `log` map on device doc (visible in ciba inspect).
134
+ // Before doc is synced, fall back to stderr.
135
+ let _logMap = null;
136
+ let _logIdx = 0;
137
+ function log(msg) {
138
+ if (_logMap) {
139
+ _logMap.set(`${Date.now()}-${_logIdx++}`, { ts: new Date().toISOString(), msg });
140
+ } else {
141
+ process.stderr.write(msg + '\n');
142
+ }
143
+ }
123
144
 
124
145
  function output(token) {
125
146
  if (jsonOut) {
@@ -159,6 +180,8 @@ await new Promise((resolve, reject) => {
159
180
  provider.on('authenticationFailed', ({ reason }) => { clearTimeout(t); reject(new Error(reason)); });
160
181
  });
161
182
 
183
+ // Switch logging to Yjs after sync — ciba inspect can now see entries.
184
+ _logMap = deviceDoc.getMap('log');
162
185
  log('→ synced');
163
186
 
164
187
  // Write public key
@@ -198,7 +221,7 @@ if (isRefresh) {
198
221
  const requests = deviceDoc.getMap('requests');
199
222
  const newRid = randomBytes(8).toString('base64url');
200
223
  log(`→ refresh — writing request ${newRid}`);
201
- requests.set(newRid, { resource, status: 'pending', created_at: new Date().toISOString() });
224
+ requests.set(newRid, { ...extraAttrs, resource, status: 'pending', created_at: new Date().toISOString() });
202
225
  provider.destroy();
203
226
  process.exit(0);
204
227
  }
@@ -217,7 +240,7 @@ if (found) {
217
240
  const requests = deviceDoc.getMap('requests');
218
241
  const newRid = randomBytes(8).toString('base64url');
219
242
  log(`→ cache miss — writing request ${newRid} resource=${resolvedResource}`);
220
- requests.set(newRid, { resource: resolvedResource, status: 'pending', created_at: new Date().toISOString() });
243
+ requests.set(newRid, { ...extraAttrs, resource: resolvedResource, status: 'pending', created_at: new Date().toISOString() });
221
244
 
222
245
  // Wait for resources to update
223
246
  log('→ waiting for token...');