troxy-cli 1.4.20 → 1.4.22

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "troxy-cli",
3
- "version": "1.4.20",
3
+ "version": "1.4.22",
4
4
  "description": "AI payment control — protect your agent's payments with policies",
5
5
  "type": "module",
6
6
  "bin": {
package/src/config.js CHANGED
@@ -14,6 +14,12 @@ export function loadConfig() {
14
14
  }
15
15
 
16
16
  export function saveConfig(data) {
17
- fs.mkdirSync(CONFIG_DIR, { recursive: true });
18
- fs.writeFileSync(CONFIG_FILE, JSON.stringify(data, null, 2));
17
+ // Owner-only permissions on dir + file — matches what AWS CLI does with
18
+ // ~/.aws/credentials. Stops other users on the same machine from reading
19
+ // the API key. mkdirSync's `mode` is only honoured for newly-created dirs,
20
+ // so we chmod afterwards too to also fix existing pre-secured installs.
21
+ fs.mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });
22
+ try { fs.chmodSync(CONFIG_DIR, 0o700); } catch {}
23
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(data, null, 2), { mode: 0o600 });
24
+ try { fs.chmodSync(CONFIG_FILE, 0o600); } catch {}
19
25
  }
package/src/policies.js CHANGED
@@ -13,7 +13,7 @@ function _scope(p) {
13
13
  const HELP = {
14
14
  list: ` troxy policies list\n\n Lists all policies in your account with their action, scope, status, and conditions.\n`,
15
15
  describe: ` troxy policies describe --name <policy-name>\n\n Shows full details for a single policy.\n\n Options:\n --name Name of the policy (use single quotes for names with special chars)\n`,
16
- create: ` troxy policies create --name <name> --action <action> [options]\n\n Creates a new policy. Login required.\n\n Required:\n --name Policy name\n --action ALLOW, BLOCK, NOTIFY, or ESCALATE\n\n Optional conditions:\n --field Field to match: amount, merchant_name, merchant_category,\n merchant_country, currency, agent_name, hour, day_of_week\n --operator eq, neq, gt, gte, lt, lte, contains, between\n --value Comparison value (e.g. 500, amazon, Monday)\n --value2 Upper bound for 'between' operator\n\n Priority:\n --priority <n> Explicit priority number (default: auto, max+10)\n Lower number = higher priority (evaluated first).\n\n Scope:\n --mcp <name> Scope policy to a specific MCP only (default: all MCPs)\n Run 'troxy mcps list' to see MCP names.\n\n Examples:\n troxy policies create --name "Block large" --action BLOCK --field amount --operator gte --value 500\n troxy policies create --name "Block Amazon" --action BLOCK --field merchant_name --operator contains --value amazon\n troxy policies create --name "Cap spend" --action BLOCK --mcp "My Laptop" --field amount --operator gte --value 200\n troxy policies create --name "Allow Wiki" --action ALLOW --priority 5 --field merchant_name --operator contains --value Wiki\n`,
16
+ create: ` troxy policies create --name <name> --action <action> [options]\n\n Creates a new policy. Login required.\n\n Required:\n --name Policy name\n --action ALLOW, BLOCK, NOTIFY, or ESCALATE\n\n Optional conditions:\n --field Field to match: amount, merchant_name, tx_per_day\n --operator eq, neq, gt, gte, lt, lte, contains, starts_with, not_contains, between\n --value Comparison value (e.g. 500, amazon)\n --value2 Upper bound for 'between' operator\n\n Priority:\n --priority <n> Explicit priority number (default: auto, max+10)\n Lower number = higher priority (evaluated first).\n\n Scope:\n --mcp <name> Scope policy to a specific MCP only (default: all MCPs)\n Run 'troxy mcps list' to see MCP names.\n\n Examples:\n troxy policies create --name "Block large" --action BLOCK --field amount --operator gte --value 500\n troxy policies create --name "Block Amazon" --action BLOCK --field merchant_name --operator contains --value amazon\n troxy policies create --name "Cap volume" --action BLOCK --field tx_per_day --operator gt --value 20\n troxy policies create --name "Allow Wiki" --action ALLOW --priority 5 --field merchant_name --operator contains --value Wiki\n`,
17
17
  'set-priority': ` troxy policies set-priority --name <policy-name> --priority <n>\n\n Changes the priority of a policy. Lower number = higher priority (evaluated first).\n\n Options:\n --name Name of the policy\n --priority New priority number (e.g. 10, 20, 50)\n\n Example:\n troxy policies set-priority --name "Block Wiki" --priority 5\n`,
18
18
  enable: ` troxy policies enable --name <policy-name>\n\n Enables a disabled policy.\n\n Options:\n --name Name of the policy to enable\n`,
19
19
  disable: ` troxy policies disable --name <policy-name>\n\n Disables a policy without deleting it.\n\n Options:\n --name Name of the policy to disable\n`,
@@ -89,12 +89,28 @@ export async function runPolicies([sub, ...args], flags) {
89
89
  console.error(' --action must be ALLOW, BLOCK, ESCALATE, or NOTIFY\n');
90
90
  process.exit(1);
91
91
  }
92
+ const VALID_FIELDS = ['amount', 'merchant_name', 'tx_per_day'];
93
+ const FIELD_OPERATORS = {
94
+ amount: ['lt', 'lte', 'gt', 'gte', 'eq', 'between'],
95
+ merchant_name: ['eq', 'neq', 'contains', 'not_contains', 'starts_with'],
96
+ tx_per_day: ['lt', 'lte', 'gt', 'gte'],
97
+ };
92
98
  const conditions = [];
93
99
  if (flags.field) {
100
+ if (!VALID_FIELDS.includes(flags.field)) {
101
+ console.error(` --field must be one of: ${VALID_FIELDS.join(', ')}\n`); process.exit(1);
102
+ }
94
103
  if (!flags.operator) { console.error(' --operator is required with --field\n'); process.exit(1); }
95
- const cond = { field: flags.field, operator: flags.operator };
96
- if (flags.value) cond.value = flags.value;
97
- if (flags.value2) cond.value2 = flags.value2;
104
+ const validOps = FIELD_OPERATORS[flags.field];
105
+ if (!validOps.includes(flags.operator)) {
106
+ console.error(` --operator for ${flags.field} must be one of: ${validOps.join(', ')}\n`); process.exit(1);
107
+ }
108
+ if (flags.value == null) { console.error(' --value is required with --field\n'); process.exit(1); }
109
+ if (flags.operator === 'between' && flags.value2 == null) {
110
+ console.error(' --value2 is required with --operator between\n'); process.exit(1);
111
+ }
112
+ const cond = { field: flags.field, operator: flags.operator, value: flags.value };
113
+ if (flags.value2 != null) cond.value2 = flags.value2;
98
114
  conditions.push(cond);
99
115
  }
100
116