@crediolabs/policy-builder-cli 0.3.1 → 0.5.0

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.
@@ -4,6 +4,10 @@
4
4
  // Usage:
5
5
  // policy-builder record --network <mainnet|testnet> --hash <tx>
6
6
  // [--xdr <b64>] [--json] [--quiet] [--out <path>]
7
+ // policy-builder declare --network <mainnet|testnet> --fn <method>
8
+ // [--token native|C...] [--max-amount <smallest-unit>]
9
+ // [--to G...,G...] [--amount-arg N] [--to-arg N]
10
+ // [--allow-zero-cap] [--json] [--quiet] [--out <path>]
7
11
  // policy-builder synthesize --recorded-tx <path.json> --network <mainnet|testnet>
8
12
  // [--responses <path.json>]
9
13
  // [--confidence <0..1>]
@@ -21,6 +25,7 @@
21
25
  // `command + subcommand-flags + global-flags` and dispatches to the matching
22
26
  // command body. Bad args -> CliError -> non-zero exit (JSON envelope under
23
27
  // --json).
28
+ import { runDeclareCommand } from "../src/commands/declare.js";
24
29
  import { runRecordCommand } from "../src/commands/record.js";
25
30
  import { runSynthesizeCommand } from "../src/commands/synthesize.js";
26
31
  import { emitCliError, parseFlags } from "../src/output.js";
@@ -37,6 +42,9 @@ async function main() {
37
42
  case 'record':
38
43
  await runRecordCommand(subcommandArgs, flags);
39
44
  return;
45
+ case 'declare':
46
+ await runDeclareCommand(subcommandArgs, flags);
47
+ return;
40
48
  case 'synthesize':
41
49
  case 'synth': {
42
50
  await runSynthesizeCommand(subcommandArgs, flags);
@@ -64,6 +72,10 @@ function printHelp() {
64
72
  Usage:
65
73
  policy-builder record --network <mainnet|testnet> --hash <tx> | --xdr <b64>
66
74
  [--json] [--quiet] [--out <path>]
75
+ policy-builder declare --network <mainnet|testnet> --fn <method>
76
+ [--token native|C...] [--max-amount <smallest-unit>]
77
+ [--to G...,G...] [--amount-arg N] [--to-arg N]
78
+ [--allow-zero-cap] [--json] [--quiet] [--out <path>]
67
79
  policy-builder synthesize --recorded-tx <path.json> --network <mainnet|testnet>
68
80
  [--responses <path.json>]
69
81
  [--confidence <0..1>]
@@ -0,0 +1,9 @@
1
+ import { type CliFlags } from '../output.ts';
2
+ interface DeclareResult {
3
+ predicate: unknown;
4
+ encodedPredicate: string;
5
+ predicateHash: string;
6
+ warnings: string[];
7
+ }
8
+ export declare function runDeclareCommand(argv: ReadonlyArray<string>, flags: CliFlags): Promise<DeclareResult>;
9
+ export {};
@@ -0,0 +1,79 @@
1
+ // packages/policy-builder-cli/src/commands/declare.ts
2
+ //
3
+ // `policy-builder declare` subcommand. The declarative counterpart to
4
+ // `synthesize`: instead of inferring a predicate from a transaction that
5
+ // happened, it takes the constraint stated outright.
6
+ //
7
+ // Thin wrapper around `runDeclarePolicy` - no business logic, just argv ->
8
+ // DeclarePolicyInput + the canonical CLI envelope.
9
+ //
10
+ // `--token native` resolves to the network's native SAC, which is why this
11
+ // command takes --network: the native SAC address differs per network, and
12
+ // pinning the wrong one produces a policy that silently matches nothing.
13
+ import { runDeclarePolicy } from '@crediolabs/policy-synth/run';
14
+ import { Asset, Networks } from '@stellar/stellar-sdk';
15
+ import { CliError, formatToolResponse, parsePairs } from "../output.js";
16
+ function missing(message) {
17
+ return new CliError({ code: 'CLI_MISSING_ARG', message, severity: 'error', retryable: false });
18
+ }
19
+ export async function runDeclareCommand(argv, flags) {
20
+ const pairs = parsePairs(argv);
21
+ const network = pairs.network;
22
+ if (network !== 'mainnet' && network !== 'testnet') {
23
+ throw missing('declare: --network <mainnet|testnet> is required');
24
+ }
25
+ if (!pairs.fn) {
26
+ throw missing('declare: --fn <method> is required (the method the policy pins)');
27
+ }
28
+ const args = { fn: pairs.fn };
29
+ if (pairs.token) {
30
+ // `native` is resolved HERE rather than in the core: the core stays pure
31
+ // and network-agnostic, and resolving it needs the passphrase.
32
+ args.contract =
33
+ pairs.token === 'native'
34
+ ? Asset.native().contractId(network === 'mainnet' ? Networks.PUBLIC : Networks.TESTNET)
35
+ : pairs.token;
36
+ }
37
+ if (pairs['max-amount'] !== undefined)
38
+ args.maxAmount = pairs['max-amount'];
39
+ if (pairs['amount-arg'] !== undefined)
40
+ args.amountArgIndex = Number(pairs['amount-arg']);
41
+ if (pairs.to !== undefined) {
42
+ args.recipients = pairs.to
43
+ .split(',')
44
+ .map((a) => a.trim())
45
+ .filter(Boolean);
46
+ }
47
+ if (pairs['to-arg'] !== undefined)
48
+ args.recipientArgIndex = Number(pairs['to-arg']);
49
+ if (pairs['allow-zero-cap'] !== undefined)
50
+ args.allowZeroCap = true;
51
+ // Slippage floor: `--min-out-ratio 99/100 --in-arg 0 --out-arg 1`. All four
52
+ // parts are required together - a ratio with no argument positions cannot
53
+ // be lowered, and guessing them would bound the wrong values silently.
54
+ if (pairs['min-out-ratio'] !== undefined) {
55
+ const [num, den] = String(pairs['min-out-ratio']).split('/');
56
+ if (!num || !den) {
57
+ throw missing('declare: --min-out-ratio must be `num/den`, e.g. 99/100 for a 1% floor');
58
+ }
59
+ if (pairs['in-arg'] === undefined || pairs['out-arg'] === undefined) {
60
+ throw missing('declare: --min-out-ratio also needs --in-arg <i> and --out-arg <j>');
61
+ }
62
+ args.minOutputRatio = {
63
+ num,
64
+ den,
65
+ inputArgIndex: Number(pairs['in-arg']),
66
+ outputArgIndex: Number(pairs['out-arg']),
67
+ };
68
+ }
69
+ const res = runDeclarePolicy(args);
70
+ const data = formatToolResponse(res, flags, 'declare');
71
+ // Warnings go to stderr so `--json` stdout stays a clean envelope, but they
72
+ // are never silent: each one names an argument index that was GUESSED, and
73
+ // a bound on the wrong argument constrains nothing while looking correct.
74
+ if (!flags.quiet) {
75
+ for (const w of data.warnings)
76
+ process.stderr.write(`warning: ${w}\n`);
77
+ }
78
+ return data;
79
+ }
@@ -1,3 +1,4 @@
1
+ export { runDeclareCommand } from './commands/declare.ts';
1
2
  export { runRecordCommand } from './commands/record.ts';
2
3
  export { runSynthesizeCommand } from './commands/synthesize.ts';
3
4
  export { formatToolResponse, readJsonFile, writeJsonFile } from './output.ts';
package/dist/src/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  // packages/policy-builder-cli/src/index.ts - public re-exports for the CLI package.
2
+ export { runDeclareCommand } from "./commands/declare.js";
2
3
  export { runRecordCommand } from "./commands/record.js";
3
4
  export { runSynthesizeCommand } from "./commands/synthesize.js";
4
5
  export { formatToolResponse, readJsonFile, writeJsonFile } from "./output.js";
@@ -0,0 +1,9 @@
1
+ import { type CliFlags } from '../output.ts';
2
+ interface DeclareResult {
3
+ predicate: unknown;
4
+ encodedPredicate: string;
5
+ predicateHash: string;
6
+ warnings: string[];
7
+ }
8
+ export declare function runDeclareCommand(argv: ReadonlyArray<string>, flags: CliFlags): Promise<DeclareResult>;
9
+ export {};
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ // packages/policy-builder-cli/src/commands/declare.ts
3
+ //
4
+ // `policy-builder declare` subcommand. The declarative counterpart to
5
+ // `synthesize`: instead of inferring a predicate from a transaction that
6
+ // happened, it takes the constraint stated outright.
7
+ //
8
+ // Thin wrapper around `runDeclarePolicy` - no business logic, just argv ->
9
+ // DeclarePolicyInput + the canonical CLI envelope.
10
+ //
11
+ // `--token native` resolves to the network's native SAC, which is why this
12
+ // command takes --network: the native SAC address differs per network, and
13
+ // pinning the wrong one produces a policy that silently matches nothing.
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.runDeclareCommand = runDeclareCommand;
16
+ const run_1 = require("@crediolabs/policy-synth/run");
17
+ const stellar_sdk_1 = require("@stellar/stellar-sdk");
18
+ const output_ts_1 = require("../output.js");
19
+ function missing(message) {
20
+ return new output_ts_1.CliError({ code: 'CLI_MISSING_ARG', message, severity: 'error', retryable: false });
21
+ }
22
+ async function runDeclareCommand(argv, flags) {
23
+ const pairs = (0, output_ts_1.parsePairs)(argv);
24
+ const network = pairs.network;
25
+ if (network !== 'mainnet' && network !== 'testnet') {
26
+ throw missing('declare: --network <mainnet|testnet> is required');
27
+ }
28
+ if (!pairs.fn) {
29
+ throw missing('declare: --fn <method> is required (the method the policy pins)');
30
+ }
31
+ const args = { fn: pairs.fn };
32
+ if (pairs.token) {
33
+ // `native` is resolved HERE rather than in the core: the core stays pure
34
+ // and network-agnostic, and resolving it needs the passphrase.
35
+ args.contract =
36
+ pairs.token === 'native'
37
+ ? stellar_sdk_1.Asset.native().contractId(network === 'mainnet' ? stellar_sdk_1.Networks.PUBLIC : stellar_sdk_1.Networks.TESTNET)
38
+ : pairs.token;
39
+ }
40
+ if (pairs['max-amount'] !== undefined)
41
+ args.maxAmount = pairs['max-amount'];
42
+ if (pairs['amount-arg'] !== undefined)
43
+ args.amountArgIndex = Number(pairs['amount-arg']);
44
+ if (pairs.to !== undefined) {
45
+ args.recipients = pairs.to
46
+ .split(',')
47
+ .map((a) => a.trim())
48
+ .filter(Boolean);
49
+ }
50
+ if (pairs['to-arg'] !== undefined)
51
+ args.recipientArgIndex = Number(pairs['to-arg']);
52
+ if (pairs['allow-zero-cap'] !== undefined)
53
+ args.allowZeroCap = true;
54
+ // Slippage floor: `--min-out-ratio 99/100 --in-arg 0 --out-arg 1`. All four
55
+ // parts are required together - a ratio with no argument positions cannot
56
+ // be lowered, and guessing them would bound the wrong values silently.
57
+ if (pairs['min-out-ratio'] !== undefined) {
58
+ const [num, den] = String(pairs['min-out-ratio']).split('/');
59
+ if (!num || !den) {
60
+ throw missing('declare: --min-out-ratio must be `num/den`, e.g. 99/100 for a 1% floor');
61
+ }
62
+ if (pairs['in-arg'] === undefined || pairs['out-arg'] === undefined) {
63
+ throw missing('declare: --min-out-ratio also needs --in-arg <i> and --out-arg <j>');
64
+ }
65
+ args.minOutputRatio = {
66
+ num,
67
+ den,
68
+ inputArgIndex: Number(pairs['in-arg']),
69
+ outputArgIndex: Number(pairs['out-arg']),
70
+ };
71
+ }
72
+ const res = (0, run_1.runDeclarePolicy)(args);
73
+ const data = (0, output_ts_1.formatToolResponse)(res, flags, 'declare');
74
+ // Warnings go to stderr so `--json` stdout stays a clean envelope, but they
75
+ // are never silent: each one names an argument index that was GUESSED, and
76
+ // a bound on the wrong argument constrains nothing while looking correct.
77
+ if (!flags.quiet) {
78
+ for (const w of data.warnings)
79
+ process.stderr.write(`warning: ${w}\n`);
80
+ }
81
+ return data;
82
+ }
@@ -1,3 +1,4 @@
1
+ export { runDeclareCommand } from './commands/declare.ts';
1
2
  export { runRecordCommand } from './commands/record.ts';
2
3
  export { runSynthesizeCommand } from './commands/synthesize.ts';
3
4
  export { formatToolResponse, readJsonFile, writeJsonFile } from './output.ts';
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
2
  // packages/policy-builder-cli/src/index.ts - public re-exports for the CLI package.
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.writeJsonFile = exports.readJsonFile = exports.formatToolResponse = exports.runSynthesizeCommand = exports.runRecordCommand = void 0;
4
+ exports.writeJsonFile = exports.readJsonFile = exports.formatToolResponse = exports.runSynthesizeCommand = exports.runRecordCommand = exports.runDeclareCommand = void 0;
5
+ var declare_ts_1 = require("./commands/declare.js");
6
+ Object.defineProperty(exports, "runDeclareCommand", { enumerable: true, get: function () { return declare_ts_1.runDeclareCommand; } });
5
7
  var record_ts_1 = require("./commands/record.js");
6
8
  Object.defineProperty(exports, "runRecordCommand", { enumerable: true, get: function () { return record_ts_1.runRecordCommand; } });
7
9
  var synthesize_ts_1 = require("./commands/synthesize.js");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crediolabs/policy-builder-cli",
3
- "version": "0.3.1",
3
+ "version": "0.5.0",
4
4
  "license": "MIT",
5
5
  "description": "CLI wrapper around the OZ policy-synth core (record + synthesize) for solo-dev and CI workflows.",
6
6
  "type": "module",
@@ -63,7 +63,7 @@
63
63
  "prepack": "bun run build"
64
64
  },
65
65
  "dependencies": {
66
- "@crediolabs/policy-synth": "0.3.1",
66
+ "@crediolabs/policy-synth": "0.5.0",
67
67
  "@stellar/stellar-sdk": "14.4.0",
68
68
  "zod": "3.25.76"
69
69
  },
@@ -0,0 +1,91 @@
1
+ // packages/policy-builder-cli/src/commands/declare.ts
2
+ //
3
+ // `policy-builder declare` subcommand. The declarative counterpart to
4
+ // `synthesize`: instead of inferring a predicate from a transaction that
5
+ // happened, it takes the constraint stated outright.
6
+ //
7
+ // Thin wrapper around `runDeclarePolicy` - no business logic, just argv ->
8
+ // DeclarePolicyInput + the canonical CLI envelope.
9
+ //
10
+ // `--token native` resolves to the network's native SAC, which is why this
11
+ // command takes --network: the native SAC address differs per network, and
12
+ // pinning the wrong one produces a policy that silently matches nothing.
13
+
14
+ import { runDeclarePolicy } from '@crediolabs/policy-synth/run'
15
+ import { Asset, Networks } from '@stellar/stellar-sdk'
16
+ import { CliError, type CliFlags, formatToolResponse, parsePairs } from '../output.ts'
17
+
18
+ interface DeclareResult {
19
+ predicate: unknown
20
+ encodedPredicate: string
21
+ predicateHash: string
22
+ warnings: string[]
23
+ }
24
+
25
+ function missing(message: string): CliError {
26
+ return new CliError({ code: 'CLI_MISSING_ARG', message, severity: 'error', retryable: false })
27
+ }
28
+
29
+ export async function runDeclareCommand(
30
+ argv: ReadonlyArray<string>,
31
+ flags: CliFlags
32
+ ): Promise<DeclareResult> {
33
+ const pairs = parsePairs(argv)
34
+ const network = pairs.network
35
+ if (network !== 'mainnet' && network !== 'testnet') {
36
+ throw missing('declare: --network <mainnet|testnet> is required')
37
+ }
38
+ if (!pairs.fn) {
39
+ throw missing('declare: --fn <method> is required (the method the policy pins)')
40
+ }
41
+
42
+ const args: Record<string, unknown> = { fn: pairs.fn }
43
+
44
+ if (pairs.token) {
45
+ // `native` is resolved HERE rather than in the core: the core stays pure
46
+ // and network-agnostic, and resolving it needs the passphrase.
47
+ args.contract =
48
+ pairs.token === 'native'
49
+ ? Asset.native().contractId(network === 'mainnet' ? Networks.PUBLIC : Networks.TESTNET)
50
+ : pairs.token
51
+ }
52
+ if (pairs['max-amount'] !== undefined) args.maxAmount = pairs['max-amount']
53
+ if (pairs['amount-arg'] !== undefined) args.amountArgIndex = Number(pairs['amount-arg'])
54
+ if (pairs.to !== undefined) {
55
+ args.recipients = (pairs.to as string)
56
+ .split(',')
57
+ .map((a) => a.trim())
58
+ .filter(Boolean)
59
+ }
60
+ if (pairs['to-arg'] !== undefined) args.recipientArgIndex = Number(pairs['to-arg'])
61
+ if (pairs['allow-zero-cap'] !== undefined) args.allowZeroCap = true
62
+ // Slippage floor: `--min-out-ratio 99/100 --in-arg 0 --out-arg 1`. All four
63
+ // parts are required together - a ratio with no argument positions cannot
64
+ // be lowered, and guessing them would bound the wrong values silently.
65
+ if (pairs['min-out-ratio'] !== undefined) {
66
+ const [num, den] = String(pairs['min-out-ratio']).split('/')
67
+ if (!num || !den) {
68
+ throw missing('declare: --min-out-ratio must be `num/den`, e.g. 99/100 for a 1% floor')
69
+ }
70
+ if (pairs['in-arg'] === undefined || pairs['out-arg'] === undefined) {
71
+ throw missing('declare: --min-out-ratio also needs --in-arg <i> and --out-arg <j>')
72
+ }
73
+ args.minOutputRatio = {
74
+ num,
75
+ den,
76
+ inputArgIndex: Number(pairs['in-arg']),
77
+ outputArgIndex: Number(pairs['out-arg']),
78
+ }
79
+ }
80
+
81
+ const res = runDeclarePolicy(args)
82
+ const data = formatToolResponse(res, flags, 'declare') as DeclareResult
83
+
84
+ // Warnings go to stderr so `--json` stdout stays a clean envelope, but they
85
+ // are never silent: each one names an argument index that was GUESSED, and
86
+ // a bound on the wrong argument constrains nothing while looking correct.
87
+ if (!flags.quiet) {
88
+ for (const w of data.warnings) process.stderr.write(`warning: ${w}\n`)
89
+ }
90
+ return data
91
+ }
package/src/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  // packages/policy-builder-cli/src/index.ts - public re-exports for the CLI package.
2
2
 
3
+ export { runDeclareCommand } from './commands/declare.ts'
3
4
  export { runRecordCommand } from './commands/record.ts'
4
5
  export { runSynthesizeCommand } from './commands/synthesize.ts'
5
6
  export { formatToolResponse, readJsonFile, writeJsonFile } from './output.ts'