@topolo/cli 0.2.3 → 0.2.4

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/dist/index.js +106 -4
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -78,6 +78,7 @@ var APP_API_URLS = {
78
78
  bugfix: "https://bugfix.topolo.app",
79
79
  bytes: "https://bytes.topolo.app",
80
80
  calendar: "https://calendar.topolo.app",
81
+ campaigns: "https://campaigns.topolo.app",
81
82
  capacity: "https://capacity.topolo.app",
82
83
  chat: "https://chat.topolo.app",
83
84
  commerce: "https://commerce.topolo.app",
@@ -336,6 +337,38 @@ var APPLICATIONS = {
336
337
  }
337
338
  ]
338
339
  },
340
+ "campaigns": {
341
+ "id": "campaigns",
342
+ "appDir": "TopoloCampaigns",
343
+ "name": "topolo-campaigns",
344
+ "packageName": "topolo-campaigns",
345
+ "productionUrl": "https://campaigns.topolo.app",
346
+ "services": [
347
+ "campaigns"
348
+ ],
349
+ "deployTargets": [
350
+ {
351
+ "name": "topolo-campaigns",
352
+ "kind": "worker",
353
+ "apiUrl": "https://campaigns.topolo.app",
354
+ "domains": [
355
+ "campaigns.topolo.app"
356
+ ],
357
+ "routes": [
358
+ "campaigns.topolo.app"
359
+ ],
360
+ "appId": "campaigns"
361
+ },
362
+ {
363
+ "name": "topolo-campaigns-api",
364
+ "kind": "worker",
365
+ "apiUrl": null,
366
+ "domains": [],
367
+ "routes": [],
368
+ "appId": null
369
+ }
370
+ ]
371
+ },
339
372
  "capacity": {
340
373
  "id": "capacity",
341
374
  "appDir": "TopoloCapacity",
@@ -2511,6 +2544,20 @@ var TopoloClient = class _TopoloClient {
2511
2544
  }
2512
2545
  async callAction(action, input2 = {}, options = {}) {
2513
2546
  const entry = await this.getAction(action);
2547
+ return this.executeResolvedAction(entry, input2, options);
2548
+ }
2549
+ async readAction(action, input2 = {}, options = {}) {
2550
+ const entry = await this.getAction(action);
2551
+ const agentAccess = entry.agentAccess ?? (entry.destructive ? "off" : entry.readOnly && !entry.requiresConfirmation ? "auto" : "confirm");
2552
+ if (entry.method !== "GET" || !entry.readOnly || entry.destructive || entry.requiresConfirmation || agentAccess !== "auto") {
2553
+ throw new TopoloPermissionError(
2554
+ `Topolo action "${entry.actionId}" is not an automatic read-only action.`,
2555
+ entry.requiredPermission ? [entry.requiredPermission] : []
2556
+ );
2557
+ }
2558
+ return this.executeResolvedAction(entry, input2, options);
2559
+ }
2560
+ async executeResolvedAction(entry, input2, options) {
2514
2561
  const agentAccess = entry.agentAccess ?? (entry.destructive ? "off" : entry.readOnly && !entry.requiresConfirmation ? "auto" : "confirm");
2515
2562
  if (agentAccess === "off") {
2516
2563
  throw new TopoloPermissionError(
@@ -3070,6 +3117,7 @@ var APP_API_URLS2 = {
3070
3117
  bugfix: "https://bugfix.topolo.app",
3071
3118
  bytes: "https://bytes.topolo.app",
3072
3119
  calendar: "https://calendar.topolo.app",
3120
+ campaigns: "https://campaigns.topolo.app",
3073
3121
  capacity: "https://capacity.topolo.app",
3074
3122
  chat: "https://chat.topolo.app",
3075
3123
  commerce: "https://commerce.topolo.app",
@@ -3394,6 +3442,46 @@ async function refreshCredentialIfNeeded(environment = selectedEnvironment(), op
3394
3442
  return cred;
3395
3443
  }
3396
3444
  }
3445
+ async function refreshContextCredentialIfNeeded(environment, key, options = {}) {
3446
+ const config = await readConfig();
3447
+ const profile = profileForEnvironment(config, environment);
3448
+ const context = profile.contexts?.[key];
3449
+ const credential = context?.credential;
3450
+ if (!credential || credential.kind !== "access_token") return credential ?? null;
3451
+ const oauthClientId = context.oauthClientId ?? profile.oauthClientId;
3452
+ if (!credential.refreshToken || !credential.expiresAt || !oauthClientId) return credential;
3453
+ const now = Math.floor(Date.now() / 1e3);
3454
+ if (!options.force && credential.expiresAt - REFRESH_SKEW_SECONDS > now) return credential;
3455
+ try {
3456
+ const oauth = new TopoloOAuth2({
3457
+ serviceUrls: resolveServiceUrls(profile.serviceUrls, environment)
3458
+ });
3459
+ const tokens = await oauth.refreshToken({
3460
+ clientId: oauthClientId,
3461
+ refreshToken: credential.refreshToken
3462
+ });
3463
+ const refreshed = {
3464
+ kind: "access_token",
3465
+ accessToken: tokens.access_token,
3466
+ ...tokens.refresh_token ? { refreshToken: tokens.refresh_token } : { refreshToken: credential.refreshToken },
3467
+ expiresAt: Math.floor(Date.now() / 1e3) + tokens.expires_in
3468
+ };
3469
+ await writeConfig(writeProfileConfig(config, environment, {
3470
+ ...profile.activeContext === key ? { credential: refreshed } : {},
3471
+ contexts: {
3472
+ ...profile.contexts ?? {},
3473
+ [key]: {
3474
+ ...context,
3475
+ credential: refreshed,
3476
+ oauthClientId
3477
+ }
3478
+ }
3479
+ }));
3480
+ return refreshed;
3481
+ } catch {
3482
+ return credential;
3483
+ }
3484
+ }
3397
3485
  function resolveAgentName(configAgentName) {
3398
3486
  return process.env.TOPOLO_AGENT_NAME ?? configAgentName;
3399
3487
  }
@@ -10714,11 +10802,25 @@ function registerContextCommand(program2) {
10714
10802
  }
10715
10803
  const wantPersonal = /^(personal|me)$/i.test(target.trim());
10716
10804
  let resolved;
10717
- if (wantPersonal) {
10805
+ let exchangeCredential = cred;
10806
+ const needle = target.trim().toLowerCase();
10807
+ const storedEntry = Object.entries(profile.contexts ?? {}).find(([, candidate]) => {
10808
+ if (wantPersonal) return candidate.contextType === "personal";
10809
+ return candidate.contextType === "organization" && (candidate.organizationId === target || candidate.organizationSlug?.toLowerCase() === needle || candidate.organizationName?.toLowerCase() === needle);
10810
+ });
10811
+ const storedContext = storedEntry?.[1];
10812
+ if (storedEntry && storedContext?.credential) {
10813
+ exchangeCredential = await refreshContextCredentialIfNeeded(environment, storedEntry[0]) ?? storedContext.credential;
10814
+ resolved = {
10815
+ contextType: storedContext.contextType,
10816
+ organizationId: storedContext.organizationId,
10817
+ organizationSlug: storedContext.organizationSlug,
10818
+ organizationName: storedContext.organizationName
10819
+ };
10820
+ } else if (wantPersonal) {
10718
10821
  resolved = { contextType: "personal" };
10719
10822
  } else {
10720
10823
  const me = await fetchMe(cred, serviceUrls);
10721
- const needle = target.trim().toLowerCase();
10722
10824
  const match = me.memberships.find(
10723
10825
  (m) => m.id === target || m.slug?.toLowerCase() === needle || m.name?.toLowerCase() === needle
10724
10826
  );
@@ -10737,13 +10839,13 @@ function registerContextCommand(program2) {
10737
10839
  }
10738
10840
  const key = contextKey(resolved.contextType, resolved.organizationId);
10739
10841
  const existing = profile.contexts?.[key];
10740
- const newCredential = await exchangeForContext(cred, serviceUrls, {
10842
+ const newCredential = await exchangeForContext(exchangeCredential, serviceUrls, {
10741
10843
  type: resolved.contextType,
10742
10844
  id: resolved.organizationId
10743
10845
  });
10744
10846
  resolved.credential = newCredential;
10745
10847
  resolved.oauthClientId = existing?.oauthClientId ?? profile.oauthClientId;
10746
- const updated = writeContextCredential(config, environment, resolved, true);
10848
+ const updated = writeContextCredential(await readConfig(), environment, resolved, true);
10747
10849
  await writeConfig(updated);
10748
10850
  emit(
10749
10851
  { environment, active: key, context: { type: resolved.contextType, label: labelFor(resolved) } },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@topolo/cli",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
4
4
  "description": "Command-line interface for Topolo developers, operators, and agents, with internal first-party scaffolding available under the topolo platform namespace.",
5
5
  "type": "module",
6
6
  "bin": {