agental 0.3.0 → 0.4.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.
Files changed (2) hide show
  1. package/dist/cli.js +97 -6
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -46,7 +46,7 @@ import { parseArgs } from "node:util";
46
46
 
47
47
  // ../src/cli-build.ts
48
48
  var PUBLISHED = true;
49
- var CLI_VERSION = true ? "0.3.0" : "dev";
49
+ var CLI_VERSION = true ? "0.4.0" : "dev";
50
50
  var DEFAULT_API_URL = true ? "https://agental.io" : "http://127.0.0.1:8741";
51
51
  var PACKAGE_NAME = "agental";
52
52
  var USER_AGENT = `agental-cli/${CLI_VERSION}`;
@@ -2407,7 +2407,7 @@ function topUpByDollars(dollars) {
2407
2407
  var TOP_UPS = [20, 50, 100, 250, 500, 1e3, 5e3, 1e4].map((dollars) => topUpByDollars(dollars));
2408
2408
 
2409
2409
  // ../src/sandbox-where.ts
2410
- var TERMS = /* @__PURE__ */ new Set(["status", "size", "region", "template", "tag", "archived", "memory", "outbound", "stops", "made-by"]);
2410
+ var TERMS = /* @__PURE__ */ new Set(["status", "size", "region", "template", "tag", "archived", "memory", "outbound", "stops", "made-by", "activity"]);
2411
2411
  function words(text) {
2412
2412
  const found = [];
2413
2413
  for (const match of text.matchAll(/(?:[^\s"]+|"[^"]*")+/g)) {
@@ -2443,8 +2443,8 @@ function whereQuery(text, query = new URLSearchParams()) {
2443
2443
  query.append(`${not ? "notMetadata" : "metadata"}[${key2}]`, value);
2444
2444
  continue;
2445
2445
  }
2446
- if (not && key2 !== "tag") {
2447
- throw new Error(`${word}: only tags and metadata keys can be left out with -.`);
2446
+ if (not && key2 !== "tag" && key2 !== "activity") {
2447
+ throw new Error(`${word}: only tags, activity and metadata keys can be left out with -.`);
2448
2448
  }
2449
2449
  switch (key2) {
2450
2450
  case "status":
@@ -2479,6 +2479,11 @@ function whereQuery(text, query = new URLSearchParams()) {
2479
2479
  case "made-by":
2480
2480
  query.append("createdBy", value);
2481
2481
  break;
2482
+ case "activity":
2483
+ for (const each of value.split(",")) {
2484
+ query.append(not ? "notActivity" : "activity", each);
2485
+ }
2486
+ break;
2482
2487
  case "archived":
2483
2488
  query.set("archived", value);
2484
2489
  break;
@@ -2623,6 +2628,8 @@ async function main() {
2623
2628
  never: { type: "boolean", default: false },
2624
2629
  outbound: { type: "string" },
2625
2630
  allow: { type: "string", multiple: true },
2631
+ // Where a sandbox's web traffic leaves, as agental create takes it: box, or residential:<country>[:sticky|rotating].
2632
+ egress: { type: "string" },
2626
2633
  "ssh-key": { type: "string", multiple: true },
2627
2634
  none: { type: "boolean", default: false },
2628
2635
  host: { type: "string", multiple: true },
@@ -2770,6 +2777,9 @@ async function main() {
2770
2777
  case "network":
2771
2778
  await network(positionals.slice(1), values);
2772
2779
  return;
2780
+ case "egress":
2781
+ await egress(positionals.slice(1), values);
2782
+ return;
2773
2783
  case "templates":
2774
2784
  await listTemplates(values.json);
2775
2785
  return;
@@ -2875,6 +2885,7 @@ async function create(values) {
2875
2885
  );
2876
2886
  }
2877
2887
  const allow = values.allow === void 0 ? void 0 : listedIn(values.allow);
2888
+ const egressAsked = values.egress === void 0 ? void 0 : egressFrom(values.egress.split(":"), "--egress takes box, or residential:<country>, as residential:de or residential:us:rotating.");
2878
2889
  const sshKeys2 = values["ssh-key"] === void 0 ? void 0 : publicKeysFrom(values["ssh-key"]);
2879
2890
  const box = boxNamed(values.box);
2880
2891
  const templateId = values.template === void 0 ? void 0 : (await templateNamed(values.template)).id;
@@ -2888,6 +2899,7 @@ async function create(values) {
2888
2899
  ...templateId ? { templateId } : {},
2889
2900
  ...regionsOption(values.region),
2890
2901
  ...allow ? { network: { allow } } : {},
2902
+ ...egressAsked ? { egress: egressAsked } : {},
2891
2903
  ...ttlSeconds === void 0 ? {} : { ttlSeconds },
2892
2904
  ...sshKeys2 === void 0 ? {} : { sshKeys: sshKeys2 },
2893
2905
  ...given
@@ -3751,6 +3763,72 @@ async function network(positionals, values) {
3751
3763
  ].join("\n")
3752
3764
  );
3753
3765
  }
3766
+ function egressFrom(words2, how) {
3767
+ const [via, country, session, ...more] = words2.map((word) => word.trim());
3768
+ if (via === "box" && country === void 0) {
3769
+ return { via: "box" };
3770
+ }
3771
+ if (via !== "residential" || country === void 0 || !/^[a-zA-Z]{2}$/.test(country) || more.length > 0 || session !== void 0 && session !== "sticky" && session !== "rotating") {
3772
+ throw new Error(how);
3773
+ }
3774
+ return { via: "residential", country: country.toLowerCase(), ...session === void 0 ? {} : { session } };
3775
+ }
3776
+ function egressInWords(body) {
3777
+ if (body?.via !== "residential") {
3778
+ return "from its server (box)";
3779
+ }
3780
+ return `residential, from ${String(body.country ?? "").toUpperCase()}, ${body.session === "rotating" ? "a new address each connection" : "one address kept (sticky)"}`;
3781
+ }
3782
+ async function egress(positionals, values) {
3783
+ const [first, ...rest] = positionals;
3784
+ const print4 = (body2, words2) => {
3785
+ process.stdout.write(values.json ? `${JSON.stringify(body2, null, 2)}
3786
+ ` : words2);
3787
+ };
3788
+ if (first === void 0 || first === "use-case" || first === "budget") {
3789
+ let body2;
3790
+ if (first === "use-case") {
3791
+ const useCase = rest.join(" ").trim();
3792
+ if (useCase === "") {
3793
+ throw new Error('egress use-case takes what your sandboxes will use residential addresses for, in a few sentences, as agental egress use-case "Our agents test sign-up flows from the countries we sell in."');
3794
+ }
3795
+ body2 = await call2("/v1/egress/use-case", { method: "PUT", body: JSON.stringify({ useCase }) });
3796
+ } else if (first === "budget") {
3797
+ const given = rest[0];
3798
+ const dollars = given === void 0 || given.trim() === "" ? Number.NaN : Number(given.replace(/^\$/, ""));
3799
+ if (rest.length !== 1 || given !== "default" && (!Number.isFinite(dollars) || dollars < 0)) {
3800
+ throw new Error("egress budget takes the most residential traffic may cost in a month, in US dollars, as agental egress budget 20, or default.");
3801
+ }
3802
+ body2 = await call2("/v1/egress/budget", { method: "PUT", body: JSON.stringify({ budgetMicros: given === "default" ? null : Math.round(dollars * 1e6) }) });
3803
+ } else {
3804
+ body2 = await call2("/v1/egress", { method: "GET" });
3805
+ }
3806
+ const account3 = body2 && typeof body2 === "object" ? body2 : {};
3807
+ const number = (name) => typeof account3[name] === "number" ? account3[name] : 0;
3808
+ const missing = Array.isArray(account3["missing"]) ? account3["missing"].map(String) : [];
3809
+ const needs = {
3810
+ plan: "a plan of Fleet ($100 a month) or larger: agental billing",
3811
+ card: "a card saved: agental billing card",
3812
+ use_case: 'what your sandboxes will use it for: agental egress use-case "<what for>"'
3813
+ };
3814
+ const refused = account3["refused"];
3815
+ const lines = !account3["offered"] ? ["Residential egress is not offered here."] : [
3816
+ `residential: ${account3["enabled"] ? "on for this account" : refused ? `refused: ${String(refused.reason ?? "")}` : "not yet"}`,
3817
+ ...missing.map((need) => ` needs ${needs[need] ?? need}`),
3818
+ `price: ${money(number("microsPerGb"))} a GB, both directions counted`,
3819
+ `this month: ${money(number("spentMicros"))} of a ${money(number("budgetMicros"))} budget${account3["budgetSet"] ? "" : " (the default)"}`,
3820
+ ...account3["blocked"] && account3["blocked"] !== "refused" ? [`blocked now: ${String(account3["blocked"]).replaceAll("_", " ")}`] : []
3821
+ ];
3822
+ print4(body2, `${lines.join("\n")}
3823
+ `);
3824
+ return;
3825
+ }
3826
+ const path4 = `/v1/sandboxes/${encodeURIComponent(first)}/egress`;
3827
+ const how = `egress takes <sandbox-id>, and then nothing, box, or residential <country> [sticky|rotating], as agental egress ${first} residential de.`;
3828
+ const body = await call2(path4, rest.length === 0 ? { method: "GET" } : { method: "PUT", body: JSON.stringify(egressFrom(rest, how)) });
3829
+ print4(body, `${first}: ${egressInWords(body)}
3830
+ `);
3831
+ }
3754
3832
  function templateSshInWords(ssh2) {
3755
3833
  const keys = ssh2.keys.length === 1 ? "1 key" : `${ssh2.keys.length} keys`;
3756
3834
  return `${ssh2.enabled ? "on" : "off"} as a sandbox is made, ${keys} let in`;
@@ -3832,7 +3910,7 @@ function showTemplate(each) {
3832
3910
  }
3833
3911
  async function template(positionals, values) {
3834
3912
  const [action, first, ...rest] = positionals;
3835
- const usage = "template takes create, show, rename, delete, start, network, ssh, ssh-keys, tags, meta, sandboxes, apply, of or put. agental help says how.";
3913
+ const usage = "template takes create, show, rename, delete, start, network, egress, ssh, ssh-keys, tags, meta, sandboxes, apply, of or put. agental help says how.";
3836
3914
  const print4 = (body, words2) => {
3837
3915
  process.stdout.write(values.json ? `${JSON.stringify(body, null, 2)}
3838
3916
  ` : words2);
@@ -3922,6 +4000,15 @@ ${mode === void 0 ? "" : "A sandbox on it takes this when it next starts, or now
3922
4000
  );
3923
4001
  return;
3924
4002
  }
4003
+ case "egress": {
4004
+ const found = await templateNamed(need(first, "<template>"));
4005
+ const path4 = `/v1/templates/${encodeURIComponent(found.id)}/egress`;
4006
+ const how = `template egress takes <template>, and then nothing, box, or residential <country> [sticky|rotating], as agental template egress ${first ?? ""} residential de.`;
4007
+ const set = await call2(path4, rest.length === 0 ? { method: "GET" } : { method: "PUT", body: JSON.stringify(egressFrom(rest, how)) });
4008
+ print4(set, `${found.name}: ${egressInWords(set)}${rest.length === 0 ? "" : ", for sandboxes made from it from now on"}
4009
+ `);
4010
+ return;
4011
+ }
3925
4012
  case "ssh": {
3926
4013
  const found = await templateNamed(need(first, "<template>"));
3927
4014
  const path4 = `/v1/templates/${encodeURIComponent(found.id)}/ssh`;
@@ -4847,7 +4934,7 @@ Setting up
4847
4934
 
4848
4935
  Usage
4849
4936
  agental create --name <name> [--template <template>] [--size tiny|small|medium|large] [--region <region>[,<region>...]] [--ttl <time> | --ttl default] [--idempotency-key <key>]
4850
- ${operator ? "[--box <name-or-id>] " : ""}[--allow <address>[,<address>...]] [--max-burst-fee <dollars>]
4937
+ ${operator ? "[--box <name-or-id>] " : ""}[--allow <address>[,<address>...]] [--egress box | residential:<country>[:sticky|rotating]] [--max-burst-fee <dollars>]
4851
4938
  [--ssh-key <public-key-or-file>]... [--tag <tag>[,<tag>...]]... [--meta <key>=<value>]...
4852
4939
  agental summary [--where '<words>'] [--tag <tag>[,<tag>...]]... [--meta <key>=<value>]... [--facets <field>[,<field>...]] [--json]
4853
4940
  agental list [--where '<words>'] [--tag <tag>[,<tag>...]]... [--meta <key>=<value>]... [--sort=<sort>] [--limit <n>] [--json]
@@ -4865,6 +4952,7 @@ Usage
4865
4952
  agental recover <sandbox-id> [--max-burst-fee <dollars>]
4866
4953
  agental auto-stop <sandbox-id> [--in <time> | --at <date-time> | --never] [--json]
4867
4954
  agental network <sandbox-id> [--allow <address>[,<address>...] | --none] [--json]
4955
+ agental egress <sandbox-id> [box | residential <country> [sticky | rotating]] [--json]
4868
4956
  agental templates [--json]
4869
4957
  agental template create <name> [--tag <tag>[,<tag>...]]... [--meta <key>=<value>]...
4870
4958
  agental template show <template> [--json]
@@ -4872,6 +4960,7 @@ Usage
4872
4960
  agental template delete <template>
4873
4961
  agental template start <template> [--size tiny|small|medium|large] [--snapshot <snapshot-id> | --new-machine] [--ttl <time> | --ttl default]
4874
4962
  agental template network <template> [open | blocked | allowlist <address>...] [--json]
4963
+ agental template egress <template> [box | residential <country> [sticky | rotating]] [--json]
4875
4964
  agental template ssh <template> [on | off] [--json]
4876
4965
  agental template ssh-keys <template> [<public-key-or-file>... | --remove <key-id>] [--json]
4877
4966
  agental template tags <template> [add|rm <tag>...] [--json]
@@ -4920,6 +5009,8 @@ Usage
4920
5009
  agental key delete <key>
4921
5010
  agental billing [prices | card | top-up <dollars> | auto-reload <dollars | off>] [--json]
4922
5011
  agental burst [budget <dollars> | budget default] [--json]
5012
+ agental egress [budget <dollars> | budget default] [--json]
5013
+ agental egress use-case "<what your sandboxes will use it for>" [--json]
4923
5014
  agental account export [--out <file>]
4924
5015
  agental ssh <sandbox-id> [--tmux] [ssh options] [command...]
4925
5016
  agental scp [scp options] <source>... <target>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agental",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "The agental command: Linux sandboxes for your agents, from the terminal. npx agental@latest signs you in and installs the agent skills.",
5
5
  "license": "MIT",
6
6
  "author": "VERTIXER LTD",