callkaro-cli-unofficial 0.6.0 → 0.6.1

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.
@@ -161,7 +161,7 @@ async function adminImpersonateCommand(clientId, opts) {
161
161
  if (existing.token && !(await (0, ui_1.confirm)(`${clientEmail} already has a stored login. Replace it with the impersonated token?`, opts.yes))) {
162
162
  return;
163
163
  }
164
- const { token, email: adminEmail } = await (0, adminAuth_1.impersonateClient)(clientId, { adminEmail: (await (0, adminAuth_1.requireAdmin)()).email });
164
+ const { token, email: adminEmail, dashboard } = await (0, adminAuth_1.impersonateClient)(clientId, { adminEmail: (await (0, adminAuth_1.requireAdmin)()).email });
165
165
  // activate: false — the admin's own account stays the default.
166
166
  (0, config_1.saveAccount)(clientEmail, {
167
167
  token,
@@ -169,11 +169,13 @@ async function adminImpersonateCommand(clientId, opts) {
169
169
  monitoring: { id: clientId, name: client.name, email: clientEmail },
170
170
  impersonated: { adminEmail, clientId },
171
171
  }, { activate: false });
172
+ (0, config_1.saveDashboardSession)(clientEmail, { email: clientEmail, ...dashboard, savedAt: new Date().toISOString() });
172
173
  (0, ui_1.success)(`Impersonating ${client.name || clientEmail} (${clientEmail}).`);
173
174
  (0, ui_1.info)(' minted a ck_ token through the admin console and stored it, scoped to that client account.');
174
175
  (0, ui_1.info)(` Use it: cku accounts use ${clientEmail}`);
175
176
  (0, ui_1.info)(` Or pin a folder: cku accounts bind ${clientEmail}`);
176
- (0, ui_1.info)(' The token renews itself by impersonating again.');
177
+ (0, ui_1.info)(' Also stored a dashboard session for it, so `cku dashboard api` reaches /v2/agents/.');
178
+ (0, ui_1.info)(' Both renew themselves by impersonating again.');
177
179
  }
178
180
  catch (err) {
179
181
  (0, ui_1.handleError)(err);
@@ -304,7 +304,14 @@ async function impersonateClient(clientId, opts = {}) {
304
304
  if (polled.status !== 'approved' || !polled.token) {
305
305
  throw new Error(`The approval session ended with status "${polled.status}" instead of an approved token.`);
306
306
  }
307
- return { token: polled.token, email: adminEmail };
307
+ // Hop 2 also produced a dashboard session for the client: the body JWT is the
308
+ // `/v2/agents/` bearer and the `access_token` cookie serves the rest. Kept,
309
+ // because function code only saves through `PUT /v2/agents/:id?version=`.
310
+ return {
311
+ token: polled.token,
312
+ email: adminEmail,
313
+ dashboard: { token: impersonationToken, sessionToken: sessionJar.get('access_token') },
314
+ };
308
315
  }
309
316
  /**
310
317
  * Re-mint an impersonated token. Used by the renewal path: an impersonated
@@ -250,7 +250,11 @@ function dashboardApi() {
250
250
  * accept either token everywhere, and guessing produces an unexplained 401.
251
251
  */
252
252
  function usesCookieAuth(path) {
253
- return !path.startsWith('/v2/agents');
253
+ // Only the list route takes the bearer. `/v2/agents/:id…` — including the
254
+ // version save `PUT /v2/agents/:id?version=` — takes the cookie and answers
255
+ // the bearer with 401 (probed 21 October 2026 with getAgentVersions).
256
+ const route = path.split('?')[0];
257
+ return route !== '/v2/agents/' && route !== '/v2/agents';
254
258
  }
255
259
  /** `?a=1&b=2`, skipping undefined and empty. Mirrors the one in api.ts. */
256
260
  function qs(params) {
@@ -153,7 +153,24 @@ async function requireDashboard() {
153
153
  // With a stored password, renew a minute early so the token cannot lapse
154
154
  // mid-request; without one a token that still has time left is still valid.
155
155
  const left = session.sessionToken ? msUntilExpiry(session.sessionToken) : 0;
156
- if (left !== undefined && left <= (session.password ? 60_000 : 0)) {
156
+ const impersonated = slug ? (0, config_1.loadAccount)(slug).impersonated : undefined;
157
+ if (left !== undefined && left <= (session.password || impersonated ? 60_000 : 0)) {
158
+ // A session minted by `cku admin impersonate` has no password; it renews by
159
+ // impersonating again, which also re-issues the account's ck_ token.
160
+ if (impersonated) {
161
+ try {
162
+ const { impersonateClient } = require('./adminAuth');
163
+ const fresh = await impersonateClient(impersonated.clientId, { adminEmail: impersonated.adminEmail });
164
+ const renewed = { email: session.email, ...fresh.dashboard, savedAt: new Date().toISOString() };
165
+ saveDashboard(email, renewed);
166
+ return { token: renewed.token, sessionToken: renewed.sessionToken, email: renewed.email };
167
+ }
168
+ catch (err) {
169
+ throw new Error(`Dashboard session for ${email} expired and re-impersonating failed: ${err.message}
170
+ ` +
171
+ ' Run: cku admin login, then cku admin impersonate ' + impersonated.clientId);
172
+ }
173
+ }
157
174
  if (!session.password) {
158
175
  const why = session.sessionToken
159
176
  ? `Dashboard session for ${email} expired ${new Date(Date.now() + left).toLocaleString('en-US')}.\n` +
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "callkaro-cli-unofficial",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Unofficial fork of the CallKaro CLI — manage your voice AI agents from the terminal",
6
6
  "bin": {