@topolo/cli 0.12.4 → 0.12.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 (2) hide show
  1. package/dist/index.js +61 -13
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ import { platform as platform2 } from "os";
9
9
  import { createInterface } from "readline/promises";
10
10
  import { stdin as input } from "process";
11
11
 
12
- // node_modules/.pnpm/@topolo+sdk@0.21.2/node_modules/@topolo/sdk/dist/index.js
12
+ // node_modules/@topolo/sdk/dist/index.js
13
13
  function applyAuthHeaders(headers, credential) {
14
14
  if (credential.kind === "api_key") {
15
15
  headers.set("X-Api-Key", credential.apiKey);
@@ -3342,7 +3342,7 @@ function createTopolo(options) {
3342
3342
  };
3343
3343
  }
3344
3344
 
3345
- // node_modules/.pnpm/@topolo+sdk@0.21.2/node_modules/@topolo/sdk/dist/node/credential-store.js
3345
+ // node_modules/@topolo/sdk/dist/node/credential-store.js
3346
3346
  import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2, chmod as chmod2, rm as rm2, stat } from "fs/promises";
3347
3347
  import { existsSync as existsSync2 } from "fs";
3348
3348
  import { homedir as homedir2, platform } from "os";
@@ -3799,10 +3799,10 @@ function runOrThrow(result, operation) {
3799
3799
  }
3800
3800
  function createPlatformInstallationSecretBackend(options = {}) {
3801
3801
  const platform22 = options.platform ?? hostPlatform();
3802
- const spawn2 = options.spawn ?? ((command, args, spawnOptions) => spawnSync(command, args, spawnOptions));
3802
+ const spawn3 = options.spawn ?? ((command, args, spawnOptions) => spawnSync(command, args, spawnOptions));
3803
3803
  const root = options.root ?? installationStorePath();
3804
3804
  if (platform22 === "darwin") {
3805
- const write = (account, value) => runOrThrow(spawn2("/usr/bin/expect", ["-c", MACOS_KEYCHAIN_WRITE_SCRIPT], {
3805
+ const write = (account, value) => runOrThrow(spawn3("/usr/bin/expect", ["-c", MACOS_KEYCHAIN_WRITE_SCRIPT], {
3806
3806
  input: `${value}
3807
3807
  `,
3808
3808
  encoding: "utf8",
@@ -3813,7 +3813,7 @@ function createPlatformInstallationSecretBackend(options = {}) {
3813
3813
  }
3814
3814
  }), "macOS Keychain write");
3815
3815
  const read = (account) => {
3816
- const result = spawn2("security", [
3816
+ const result = spawn3("security", [
3817
3817
  "find-generic-password",
3818
3818
  "-a",
3819
3819
  account,
@@ -3825,7 +3825,7 @@ function createPlatformInstallationSecretBackend(options = {}) {
3825
3825
  return runOrThrow(result, "macOS Keychain read");
3826
3826
  };
3827
3827
  const remove = (account) => {
3828
- const result = spawn2("security", [
3828
+ const result = spawn3("security", [
3829
3829
  "delete-generic-password",
3830
3830
  "-a",
3831
3831
  account,
@@ -3875,7 +3875,7 @@ function createPlatformInstallationSecretBackend(options = {}) {
3875
3875
  if (platform22 === "linux") {
3876
3876
  return {
3877
3877
  async set(account, value) {
3878
- runOrThrow(spawn2("secret-tool", [
3878
+ runOrThrow(spawn3("secret-tool", [
3879
3879
  "store",
3880
3880
  "--label=Topolo external agent installation",
3881
3881
  "service",
@@ -3885,7 +3885,7 @@ function createPlatformInstallationSecretBackend(options = {}) {
3885
3885
  ], { input: value, encoding: "utf8" }), "Linux Secret Service write");
3886
3886
  },
3887
3887
  async get(account) {
3888
- const result = spawn2("secret-tool", [
3888
+ const result = spawn3("secret-tool", [
3889
3889
  "lookup",
3890
3890
  "service",
3891
3891
  KEYCHAIN_SERVICE,
@@ -3896,7 +3896,7 @@ function createPlatformInstallationSecretBackend(options = {}) {
3896
3896
  return runOrThrow(result, "Linux Secret Service read");
3897
3897
  },
3898
3898
  async delete(account) {
3899
- const result = spawn2("secret-tool", [
3899
+ const result = spawn3("secret-tool", [
3900
3900
  "clear",
3901
3901
  "service",
3902
3902
  KEYCHAIN_SERVICE,
@@ -3909,7 +3909,7 @@ function createPlatformInstallationSecretBackend(options = {}) {
3909
3909
  }
3910
3910
  if (platform22 === "win32") {
3911
3911
  const secretPath = (account) => join(root, ".secrets", `${account.replace(":", "-")}.bin`);
3912
- const powershell = (script, path, input2) => spawn2(
3912
+ const powershell = (script, path, input2) => spawn3(
3913
3913
  "powershell.exe",
3914
3914
  ["-NoProfile", "-NonInteractive", "-Command", script],
3915
3915
  { input: input2, encoding: "utf8", env: { ...process.env, TOPOLO_SECRET_PATH: path } }
@@ -4748,7 +4748,9 @@ function formatPretty(info) {
4748
4748
  }
4749
4749
 
4750
4750
  // src/commands/actions.ts
4751
+ import { spawn as spawn2 } from "child_process";
4751
4752
  import { readFileSync as readFileSync2 } from "fs";
4753
+ import { platform as platform3 } from "os";
4752
4754
  import { basename, extname } from "path";
4753
4755
 
4754
4756
  // src/commands/api.ts
@@ -4888,7 +4890,7 @@ function registerActionsCommand(program2) {
4888
4890
  fail2(err, mode);
4889
4891
  }
4890
4892
  });
4891
- actions.command("call <action>").description("Call a permission-scoped Topolo action").option("-d, --data <json-or-@file>", "JSON action input or @path to a JSON file", "{}").option("--resource-type <type>", "App-local resource alias, such as workspace").option("--resource-id <id>", "Resource id for the selected resource type").option("--confirm", "Acknowledge a mutating action").option("--detail", "Return the complete action response instead of a bounded agent projection").option("--json", "Output JSON").option("--no-json", "Output human-readable text").action(async (action, opts, command) => {
4893
+ actions.command("call <action>").description("Call a permission-scoped Topolo action").option("-d, --data <json-or-@file>", "JSON action input or @path to a JSON file", "{}").option("--resource-type <type>", "App-local resource alias, such as workspace").option("--resource-id <id>", "Resource id for the selected resource type").option("--confirm", "Acknowledge a mutating action").option("--no-browser", "Print an approval verification URL without opening it").option("--detail", "Return the complete action response instead of a bounded agent projection").option("--json", "Output JSON").option("--no-json", "Output human-readable text").action(async (action, opts, command) => {
4892
4894
  const mode = resolveOutputMode(opts.json ?? command.parent?.opts().json);
4893
4895
  try {
4894
4896
  const topolo = await buildClient();
@@ -4901,8 +4903,16 @@ function registerActionsCommand(program2) {
4901
4903
  confirm: opts.confirm === true,
4902
4904
  ...resource ? { resource } : {}
4903
4905
  });
4904
- const output = detailRequested(opts.detail, command) ? result : compactActionResult(action, result);
4905
- emit(output, mode, () => JSON.stringify(output, null, 2));
4906
+ const approval = externalApprovalHandoff(action, result);
4907
+ const detailed = detailRequested(opts.detail, command);
4908
+ const output = detailed ? result : {
4909
+ ...compactActionResult(action, result),
4910
+ ...approval ? { approval } : {}
4911
+ };
4912
+ const opened = approval && mode === "pretty" && opts.browser !== false ? tryOpenApproval(approval.verificationUrl) : false;
4913
+ emit(output, mode, () => approval ? `${JSON.stringify(output, null, 2)}
4914
+
4915
+ Approval required. ${opened ? "Opening" : "Open"} ${approval.verificationUrl}` : JSON.stringify(output, null, 2));
4906
4916
  } catch (err) {
4907
4917
  fail2(err, mode);
4908
4918
  }
@@ -5023,6 +5033,44 @@ function registerActionsCommand(program2) {
5023
5033
  }
5024
5034
  });
5025
5035
  }
5036
+ function externalApprovalHandoff(action, result) {
5037
+ if (!action.includes("agent_invocations.")) return null;
5038
+ if (!result || typeof result !== "object" || Array.isArray(result)) return null;
5039
+ const invocation = result.invocation;
5040
+ if (!invocation || typeof invocation !== "object" || Array.isArray(invocation)) return null;
5041
+ const record2 = invocation;
5042
+ if (record2.status !== "awaiting_approval") return null;
5043
+ const approval = record2.approval;
5044
+ if (!approval || typeof approval !== "object" || Array.isArray(approval)) return null;
5045
+ const handoff = approval;
5046
+ if (typeof handoff.requestId !== "string" || typeof handoff.verificationUrl !== "string") return null;
5047
+ try {
5048
+ const url = new URL(handoff.verificationUrl);
5049
+ if (url.protocol !== "https:" || !/(^|\.)topolo\.(?:app|us|dev)$/.test(url.hostname)) return null;
5050
+ } catch {
5051
+ return null;
5052
+ }
5053
+ return {
5054
+ requestId: handoff.requestId,
5055
+ expiresAt: typeof handoff.expiresAt === "string" ? handoff.expiresAt : null,
5056
+ verificationUrl: handoff.verificationUrl
5057
+ };
5058
+ }
5059
+ function tryOpenApproval(url) {
5060
+ try {
5061
+ const os = platform3();
5062
+ const command = os === "darwin" ? "open" : os === "win32" ? "start" : "xdg-open";
5063
+ const child = spawn2(command, os === "win32" ? ["", url] : [url], {
5064
+ detached: true,
5065
+ stdio: "ignore",
5066
+ ...os === "win32" ? { shell: true } : {}
5067
+ });
5068
+ child.unref();
5069
+ return true;
5070
+ } catch {
5071
+ return false;
5072
+ }
5073
+ }
5026
5074
  async function actionExecutionContext(topolo) {
5027
5075
  const environment = selectedEnvironment();
5028
5076
  const config = await readConfig();
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@topolo/cli",
3
3
  "packageManager": "pnpm@10.17.1",
4
- "version": "0.12.4",
5
- "topoloPublishedVersion": "0.12.4",
4
+ "version": "0.12.6",
5
+ "topoloPublishedVersion": "0.12.6",
6
6
  "description": "Public command-line interface for Topolo customer organizations, developers, and agents.",
7
7
  "license": "UNLICENSED",
8
8
  "author": "Topolo",