@topolo/cli 0.9.2 → 0.9.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 +87 -46
  2. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -1546,7 +1546,9 @@ var TopoloClient = class _TopoloClient {
1546
1546
  };
1547
1547
  }
1548
1548
  planResolvedAction(entry, input2, options) {
1549
- const validation = validateTopoloActionInput(entry.inputSchema, input2);
1549
+ const inputValidation = validateTopoloActionInput(entry.inputSchema, input2);
1550
+ const resourceIssue = validateActionResource(entry, options.resource);
1551
+ const validation = resourceIssue ? { valid: false, errors: [...inputValidation.errors, resourceIssue] } : inputValidation;
1550
1552
  const agentAccess = entry.agentAccess ?? (entry.destructive ? "off" : entry.readOnly && !entry.requiresConfirmation ? "auto" : "confirm");
1551
1553
  const confirmationRequired = agentAccess === "confirm" || entry.requiresConfirmation || entry.destructive;
1552
1554
  let request = null;
@@ -1605,6 +1607,13 @@ var TopoloClient = class _TopoloClient {
1605
1607
  `Topolo action "${entry.actionId}" input is invalid: ${validation.errors.map((issue) => `${issue.path} ${issue.message}`).join("; ")}`
1606
1608
  );
1607
1609
  }
1610
+ const resourceIssue = validateActionResource(entry, options.resource);
1611
+ if (resourceIssue) {
1612
+ throw new TopoloSdkError(
1613
+ "invalid_action_resource",
1614
+ `Topolo action "${entry.actionId}" resource context is invalid: ${resourceIssue.message}`
1615
+ );
1616
+ }
1608
1617
  const mapped = mapActionInput(entry, input2);
1609
1618
  const service = entry.appSlug || entry.appId;
1610
1619
  const result = await this.request({
@@ -1667,6 +1676,32 @@ var TopoloClient = class _TopoloClient {
1667
1676
  };
1668
1677
  }
1669
1678
  };
1679
+ function validateActionResource(entry, resource) {
1680
+ const permissionName = entry.permissionName || entry.requiredPermission.split(".").at(-1) || "";
1681
+ if (!permissionName.startsWith("workspace:")) return null;
1682
+ if (!resource) {
1683
+ return {
1684
+ path: "$.resource",
1685
+ code: "required",
1686
+ message: "Select a workspace with the application resource catalog before planning or calling this action."
1687
+ };
1688
+ }
1689
+ if (resource.resourceType !== "workspace") {
1690
+ return {
1691
+ path: "$.resource.resourceType",
1692
+ code: "const",
1693
+ message: 'This action requires resourceType "workspace".'
1694
+ };
1695
+ }
1696
+ if (!resource.resourceId.trim()) {
1697
+ return {
1698
+ path: "$.resource.resourceId",
1699
+ code: "required",
1700
+ message: "This action requires a workspace resourceId."
1701
+ };
1702
+ }
1703
+ return null;
1704
+ }
1670
1705
  function resolveConfiguredAppId(slug, appIds) {
1671
1706
  for (const serviceKey of serviceKeyCandidates2(slug)) {
1672
1707
  const configured = appIds?.[serviceKey];
@@ -2942,52 +2977,58 @@ async function refreshCredentialIfNeeded(environment = selectedEnvironment(), op
2942
2977
  if (process.env[`${prefix}_API_KEY`] || process.env[`${prefix}_ACCESS_TOKEN`] || environment === "production" && process.env.TOPOLO_API_KEY || environment === "production" && process.env.TOPOLO_ACCESS_TOKEN) {
2943
2978
  return resolveCredential(environment);
2944
2979
  }
2945
- const config = await readConfig();
2946
- const profile = profileForEnvironment(config, environment);
2947
- const cred = profile.credential;
2948
- if (!cred || cred.kind !== "access_token") return cred ?? null;
2949
- if (!cred.refreshToken || !profile.oauthClientId) return cred;
2950
- const now = Math.floor(Date.now() / 1e3);
2951
- if (!options.force && cred.expiresAt && cred.expiresAt - REFRESH_SKEW_SECONDS > now) return cred;
2952
- const serviceUrls = resolveServiceUrls(profile.serviceUrls, environment);
2953
- if (environment === "local" && !serviceUrls?.auth) {
2954
- throw new TopoloSdkError2(
2955
- "environment_service_url_required",
2956
- "Local Topolo credential refresh requires an explicit local Auth service origin."
2957
- );
2958
- }
2959
- try {
2960
- const oauth = new TopoloOAuth2({
2961
- serviceUrls
2962
- });
2963
- const tokens = await oauth.refreshToken({
2964
- clientId: profile.oauthClientId,
2965
- refreshToken: cred.refreshToken
2966
- });
2967
- const refreshed = {
2968
- kind: "access_token",
2969
- accessToken: tokens.access_token,
2970
- ...tokens.refresh_token ? { refreshToken: tokens.refresh_token } : { refreshToken: cred.refreshToken },
2971
- expiresAt: Math.floor(Date.now() / 1e3) + tokens.expires_in
2972
- };
2973
- const activeContext = profile.activeContext;
2974
- const activeEntry = activeContext ? profile.contexts?.[activeContext] : void 0;
2975
- const updated = activeContext && activeEntry ? writeProfileConfig(config, environment, {
2976
- credential: refreshed,
2977
- contexts: {
2978
- ...profile.contexts ?? {},
2979
- [activeContext]: {
2980
- ...activeEntry,
2981
- credential: refreshed,
2982
- oauthClientId: activeEntry.oauthClientId ?? profile.oauthClientId
2980
+ const observed = profileForEnvironment(await readConfig(), environment).credential;
2981
+ return withConfigWriteLock(async () => {
2982
+ const config = await readConfig();
2983
+ const profile = profileForEnvironment(config, environment);
2984
+ const cred = profile.credential;
2985
+ if (!cred || cred.kind !== "access_token") return cred ?? null;
2986
+ if (!cred.refreshToken || !profile.oauthClientId) return cred;
2987
+ if (options.force && observed?.kind === "access_token" && observed.accessToken !== cred.accessToken) {
2988
+ return cred;
2989
+ }
2990
+ const now = Math.floor(Date.now() / 1e3);
2991
+ if (!options.force && cred.expiresAt && cred.expiresAt - REFRESH_SKEW_SECONDS > now) return cred;
2992
+ const serviceUrls = resolveServiceUrls(profile.serviceUrls, environment);
2993
+ if (environment === "local" && !serviceUrls?.auth) {
2994
+ throw new TopoloSdkError2(
2995
+ "environment_service_url_required",
2996
+ "Local Topolo credential refresh requires an explicit local Auth service origin."
2997
+ );
2998
+ }
2999
+ try {
3000
+ const oauth = new TopoloOAuth2({
3001
+ serviceUrls
3002
+ });
3003
+ const tokens = await oauth.refreshToken({
3004
+ clientId: profile.oauthClientId,
3005
+ refreshToken: cred.refreshToken
3006
+ });
3007
+ const refreshed = {
3008
+ kind: "access_token",
3009
+ accessToken: tokens.access_token,
3010
+ ...tokens.refresh_token ? { refreshToken: tokens.refresh_token } : { refreshToken: cred.refreshToken },
3011
+ expiresAt: Math.floor(Date.now() / 1e3) + tokens.expires_in
3012
+ };
3013
+ const activeContext = profile.activeContext;
3014
+ const activeEntry = activeContext ? profile.contexts?.[activeContext] : void 0;
3015
+ const updated = activeContext && activeEntry ? writeProfileConfig(config, environment, {
3016
+ credential: refreshed,
3017
+ contexts: {
3018
+ ...profile.contexts ?? {},
3019
+ [activeContext]: {
3020
+ ...activeEntry,
3021
+ credential: refreshed,
3022
+ oauthClientId: activeEntry.oauthClientId ?? profile.oauthClientId
3023
+ }
2983
3024
  }
2984
- }
2985
- }) : environment === "production" && !config.profiles?.production ? { ...config, credential: refreshed } : writeProfileConfig(config, environment, { credential: refreshed });
2986
- await writeConfig(updated);
2987
- return refreshed;
2988
- } catch {
2989
- return cred;
2990
- }
3025
+ }) : environment === "production" && !config.profiles?.production ? { ...config, credential: refreshed } : writeProfileConfig(config, environment, { credential: refreshed });
3026
+ await writeConfig(updated);
3027
+ return refreshed;
3028
+ } catch {
3029
+ return cred;
3030
+ }
3031
+ });
2991
3032
  }
2992
3033
  async function refreshContextCredentialIfNeeded(environment, key, options = {}) {
2993
3034
  return withConfigWriteLock(async () => {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@topolo/cli",
3
- "version": "0.9.2",
4
- "topoloPublishedVersion": "0.9.2",
3
+ "version": "0.9.4",
4
+ "topoloPublishedVersion": "0.9.4",
5
5
  "description": "Public command-line interface for Topolo customer organizations, developers, and agents.",
6
6
  "license": "UNLICENSED",
7
7
  "author": "Topolo",
@@ -25,8 +25,8 @@
25
25
  },
26
26
  "dependencies": {
27
27
  "@modelcontextprotocol/sdk": "^1.0.0",
28
- "@topolo/mcp": "0.11.9",
29
- "@topolo/sdk": "0.11.9",
28
+ "@topolo/mcp": "0.11.11",
29
+ "@topolo/sdk": "0.11.11",
30
30
  "commander": "^12.1.0"
31
31
  },
32
32
  "devDependencies": {