@usenaive-sdk/vetta-cli 0.1.0 → 0.3.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 (3) hide show
  1. package/README.md +5 -3
  2. package/dist/index.js +140 -18
  3. package/package.json +11 -12
package/README.md CHANGED
@@ -1,7 +1,8 @@
1
1
  # @usenaive-sdk/vetta-cli
2
2
 
3
3
  The `vetta` command line — drive the whole Vetta API from a terminal or a script: agents, sessions,
4
- computers, skills, files, identities, vaults, comms, deployments, webhooks and credits.
4
+ computers, skills, files, identities, vaults, comms, deployments, webhooks, the monthly plan and
5
+ credits.
5
6
 
6
7
  It is built on [`@usenaive-sdk/vetta`](https://www.npmjs.com/package/@usenaive-sdk/vetta), the same typed
7
8
  client the dashboard uses, so the CLI and the API never drift apart.
@@ -17,6 +18,7 @@ npm i -g @usenaive-sdk/vetta-cli
17
18
  ```bash
18
19
  vetta login --api-key sk_… # or: vetta login --email … --password …
19
20
  vetta whoami
21
+ vetta plan show # an org with no plan is refused everywhere else
20
22
  ```
21
23
 
22
24
  Credentials are stored on a named profile, so several organizations can be driven from one machine
@@ -29,8 +31,8 @@ vetta <group> <command> [flags]
29
31
  ```
30
32
 
31
33
  Groups: `agent` `audit` `auth` `computer` `connections` `credits` `deploy` `domain` `email` `file`
32
- `identity` `keys` `login` `logout` `models` `org` `phone` `run` `session` `skill` `vault` `webhook`
33
- `whoami`.
34
+ `identity` `keys` `login` `logout` `models` `org` `phone` `plan` `run` `session` `skill` `vault`
35
+ `webhook` `whoami`.
34
36
 
35
37
  `vetta <group> --help` lists a group's commands.
36
38
 
package/dist/index.js CHANGED
@@ -23448,6 +23448,10 @@ var SubscriptionSchema = external_exports.object({
23448
23448
  cancel_at_period_end: external_exports.boolean(),
23449
23449
  action_url: external_exports.string().nullable()
23450
23450
  });
23451
+ var BillingPortalSessionSchema = external_exports.object({
23452
+ object: external_exports.literal("billing_portal_session"),
23453
+ action_url: external_exports.string()
23454
+ });
23451
23455
  var AcknowledgedSchema = external_exports.object({
23452
23456
  object: external_exports.string(),
23453
23457
  delivery: external_exports.enum(["sent", "undeliverable"])
@@ -23455,7 +23459,12 @@ var AcknowledgedSchema = external_exports.object({
23455
23459
  var auth = (config2) => ({
23456
23460
  /** Who this credential is. No scope, no org id needed — that is the whole point (§20.1). */
23457
23461
  me: () => request(config2, IdentitySchema22, { method: "GET", path: "/v1/me" }),
23458
- /** Sign in, or re-scope the session already held to another organization (§20.4). */
23462
+ /**
23463
+ * Sign in, finish an external sign-in, or re-scope the session already held to another
23464
+ * organization (§20.4). `provider_token` is the access token an external provider sign-in came
23465
+ * back with; the control plane re-verifies it against the provider and refuses an address the
23466
+ * provider has not proved.
23467
+ */
23459
23468
  session: (body) => request(config2, AuthSessionSchema, { method: "POST", path: "/v1/auth/session", body }),
23460
23469
  register: (body) => request(
23461
23470
  config2,
@@ -23531,7 +23540,17 @@ var credits = (config2) => ({
23531
23540
  * Stops the plan at the end of the period already paid for — never immediately. The state
23532
23541
  * follows the provider's callback, so the answer reports the request, not a completed change.
23533
23542
  */
23534
- cancelSubscription: () => request(config2, SubscriptionSchema, { method: "DELETE", path: "/v1/credits/subscription" })
23543
+ cancelSubscription: () => request(config2, SubscriptionSchema, { method: "DELETE", path: "/v1/credits/subscription" }),
23544
+ /**
23545
+ * A link to the hosted account page: change the card, read the invoices, cancel. Opens nothing
23546
+ * and changes nothing — an organization that never subscribed has no account, and this answers
23547
+ * `404 not_found` rather than an empty page.
23548
+ */
23549
+ billingPortal: () => request(config2, BillingPortalSessionSchema, {
23550
+ method: "POST",
23551
+ path: "/v1/credits/billing_portal",
23552
+ body: {}
23553
+ })
23535
23554
  });
23536
23555
  var auditLogs = (config2) => ({
23537
23556
  list: (query = {}) => request(config2, page(AuditEntrySchema2), { method: "GET", path: "/v1/audit_logs", query })
@@ -23678,14 +23697,23 @@ var phones = (config2) => ({
23678
23697
  body
23679
23698
  })
23680
23699
  });
23681
- var CONNECTORS = ["email", "chat", "tracker", "crm", "docs"];
23682
23700
  var CONNECTION_AUTHS = ["managed_oauth", "oauth", "api_key"];
23701
+ var ConnectionFieldSchema = external_exports.object({
23702
+ name: external_exports.string(),
23703
+ label: external_exports.string(),
23704
+ description: external_exports.string(),
23705
+ required: external_exports.boolean()
23706
+ });
23683
23707
  var AuthConfigSchema = external_exports.object({
23684
23708
  object: external_exports.literal("auth_config"),
23685
23709
  id: external_exports.string(),
23686
23710
  connector: external_exports.string(),
23687
23711
  auth: external_exports.enum(CONNECTION_AUTHS),
23688
23712
  scopes: external_exports.array(external_exports.string()),
23713
+ /** The operations a connection made from this config may call. Pinned at creation. */
23714
+ tools: external_exports.array(external_exports.string()),
23715
+ /** Supply these to `connect`, or the hosted page asks the person for them instead. */
23716
+ connect_fields: external_exports.array(ConnectionFieldSchema),
23689
23717
  created_at: external_exports.string()
23690
23718
  });
23691
23719
  var ConnectionSchema = external_exports.object({
@@ -23702,7 +23730,38 @@ var ConnectionSchema = external_exports.object({
23702
23730
  last_refreshed_at: external_exports.string().nullable(),
23703
23731
  created_at: external_exports.string()
23704
23732
  });
23733
+ var ConnectionAppSchema = external_exports.object({
23734
+ object: external_exports.literal("connection_app"),
23735
+ slug: external_exports.string(),
23736
+ name: external_exports.string(),
23737
+ description: external_exports.string(),
23738
+ categories: external_exports.array(external_exports.string()),
23739
+ /** Which setup paths work: `managed_oauth` is one call, `oauth` needs your own app first. */
23740
+ auth: external_exports.array(external_exports.enum(CONNECTION_AUTHS)),
23741
+ tool_count: external_exports.number()
23742
+ });
23743
+ var ConnectionAppDetailSchema = ConnectionAppSchema.extend({
23744
+ /** The operations pinned when `tools` is not given to `createAuthConfig`. */
23745
+ default_tools: external_exports.array(external_exports.string()),
23746
+ connect_fields: external_exports.array(ConnectionFieldSchema)
23747
+ });
23748
+ var ConnectionAppPage = external_exports.object({
23749
+ object: external_exports.literal("list"),
23750
+ data: external_exports.array(ConnectionAppSchema),
23751
+ has_more: external_exports.boolean(),
23752
+ next_cursor: external_exports.string().nullable()
23753
+ });
23705
23754
  var connections = (config2) => ({
23755
+ /**
23756
+ * Search the app catalogue — the first call in the flow, and the one that replaced the enum.
23757
+ * `search` matches an app's name, id or description; the reply's `slug` is what `connector` takes.
23758
+ */
23759
+ listApps: (query = {}) => request(config2, ConnectionAppPage, { method: "GET", path: "/v1/connections/apps", query }),
23760
+ /** One app, whole: auth methods, the operations pinned by default, what `connect` will ask for. */
23761
+ getApp: (slug) => request(config2, ConnectionAppDetailSchema, {
23762
+ method: "GET",
23763
+ path: `/v1/connections/apps/${slug}`
23764
+ }),
23706
23765
  /** Org-level: *how* a connector authenticates. Shared across every identity that connects it. */
23707
23766
  createAuthConfig: (body) => request(config2, AuthConfigSchema, {
23708
23767
  method: "POST",
@@ -24638,23 +24697,46 @@ var computer = {
24638
24697
  // src/commands/connections.ts
24639
24698
  function authConfigBody(args) {
24640
24699
  const connector = args.need("connector");
24641
- if (!CONNECTORS.includes(connector)) {
24642
- throw new UsageError(`--connector must be one of: ${CONNECTORS.join(", ")}`);
24643
- }
24644
24700
  const auth3 = args.str("auth");
24645
24701
  if (auth3 !== void 0 && !CONNECTION_AUTHS.includes(auth3)) {
24646
24702
  throw new UsageError(`--auth must be one of: ${CONNECTION_AUTHS.join(", ")}`);
24647
24703
  }
24648
24704
  const clientId = args.str("client-id");
24649
24705
  const scopes2 = args.list("scope");
24706
+ const tools2 = args.list("tool");
24650
24707
  return {
24651
24708
  connector,
24652
24709
  ...auth3 === void 0 ? {} : { auth: auth3 },
24653
24710
  ...scopes2.length === 0 ? {} : { scopes: scopes2 },
24711
+ ...tools2.length === 0 ? {} : { tools: tools2 },
24654
24712
  ...clientId === void 0 ? {} : { client_id: clientId }
24655
24713
  };
24656
24714
  }
24715
+ function fieldsFrom(args) {
24716
+ const out = {};
24717
+ for (const pair of args.list("field")) {
24718
+ const at = pair.indexOf("=");
24719
+ if (at < 1) throw new UsageError("--field takes name=value, e.g. --field subdomain=acme");
24720
+ out[pair.slice(0, at)] = pair.slice(at + 1);
24721
+ }
24722
+ return out;
24723
+ }
24657
24724
  var connections2 = {
24725
+ /**
24726
+ * Search the catalogue. This is where a setup starts, and it is the command the five-word enum
24727
+ * made impossible: `--search helpdesk` finds what "triage my support tickets" needs.
24728
+ */
24729
+ apps: async ({ client, args }) => ({
24730
+ out: await client.connections.listApps({
24731
+ search: args.str("search"),
24732
+ limit: args.int("limit"),
24733
+ after: args.str("after")
24734
+ })
24735
+ }),
24736
+ /** One app whole: the auth methods that work, the operations pinned by default, what `connect` asks for. */
24737
+ app: async ({ client, args }) => ({
24738
+ out: await client.connections.getApp(args.str("app") ?? args.at(0, "app-id"))
24739
+ }),
24658
24740
  list: async ({ client, args }) => {
24659
24741
  const identity3 = args.str("identity");
24660
24742
  return { out: await client.connections.list(identity3 === void 0 ? {} : { identity: identity3 }) };
@@ -24673,12 +24755,16 @@ var connections2 = {
24673
24755
  })
24674
24756
  };
24675
24757
  var passport = {
24676
- connect: async ({ client, args }) => ({
24677
- out: await client.connections.connect({
24678
- auth_config_id: args.need("auth-config"),
24679
- identity: args.str("identity") ?? args.at(0, "identity")
24680
- })
24681
- }),
24758
+ connect: async ({ client, args }) => {
24759
+ const fields2 = fieldsFrom(args);
24760
+ return {
24761
+ out: await client.connections.connect({
24762
+ auth_config_id: args.need("auth-config"),
24763
+ identity: args.str("identity") ?? args.at(0, "identity"),
24764
+ ...Object.keys(fields2).length === 0 ? {} : { fields: fields2 }
24765
+ })
24766
+ };
24767
+ },
24682
24768
  /** Revoke. The provider drops the stored credential and every later call is refused. */
24683
24769
  disconnect: async ({ client, args }) => ({
24684
24770
  out: await client.connections.disconnect(args.str("connection") ?? args.at(0, "connection-id"))
@@ -24711,6 +24797,26 @@ var credits2 = {
24711
24797
  })
24712
24798
  })
24713
24799
  };
24800
+ var plan = {
24801
+ /** `status`, when the paid period ends, and whether it is already set to stop (§18b). */
24802
+ show: async ({ client }) => ({ out: await client.credits.subscription() }),
24803
+ /**
24804
+ * Opens the hosted checkout and changes **nothing**, exactly like `credits topup`: the plan turns
24805
+ * `active` only when the signed provider callback lands, whenever the customer gets round to
24806
+ * paying. So this prints `action_url` whole and returns, rather than blocking on a purchase it
24807
+ * cannot see, cannot complete and cannot cancel. `vetta plan show` is what reports the outcome.
24808
+ */
24809
+ subscribe: async ({ client }) => ({ out: await client.credits.subscribe() }),
24810
+ /**
24811
+ * The hosted account page — card, invoices, cancellation. Symmetric with `subscribe`: it prints
24812
+ * an `action_url` a human opens and returns, because there is nothing here a terminal can drive.
24813
+ *
24814
+ * It is why `plan` still has no `cancel` command. A cancel command would be a second, narrower
24815
+ * way to do one of the four things this page already does, and it would leave the other three —
24816
+ * a dead card is what actually ends most plans — reachable only from the dashboard.
24817
+ */
24818
+ portal: async ({ client }) => ({ out: await client.credits.billingPortal() })
24819
+ };
24714
24820
 
24715
24821
  // src/commands/deploy.ts
24716
24822
  function fields(args) {
@@ -24969,8 +25075,16 @@ function exitForStopReason(reason) {
24969
25075
  function exitForError(cause) {
24970
25076
  return cause instanceof UsageError ? EXIT_USAGE : EXIT_ERROR;
24971
25077
  }
25078
+ var REMEDY = {
25079
+ subscription_required: "vetta plan subscribe"
25080
+ };
24972
25081
  function describeError(cause) {
24973
- if (cause instanceof ApiError) return `error: ${cause.code}: ${cause.message} (${cause.requestId})`;
25082
+ if (cause instanceof ApiError) {
25083
+ const remedy = REMEDY[cause.code];
25084
+ const fix = remedy === void 0 ? "" : `
25085
+ fix it with: ${remedy}`;
25086
+ return `error: ${cause.code}: ${cause.message} (${cause.requestId})${fix}`;
25087
+ }
24974
25088
  if (cause instanceof UsageError) return `usage: ${cause.message}`;
24975
25089
  return `error: ${cause instanceof Error ? cause.message : String(cause)}`;
24976
25090
  }
@@ -25309,7 +25423,8 @@ var GROUPS = {
25309
25423
  webhook,
25310
25424
  org,
25311
25425
  keys: keys2,
25312
- credits: credits2
25426
+ credits: credits2,
25427
+ plan
25313
25428
  };
25314
25429
  var FLAGS = {
25315
25430
  auth: {
@@ -25362,7 +25477,7 @@ var FLAGS = {
25362
25477
  attach: "agent identity",
25363
25478
  detach: "agent identity",
25364
25479
  // Reached as `vetta identity connect|disconnect`; the sub-groups below carry the rest.
25365
- connect: "identity auth-config",
25480
+ connect: "identity auth-config field",
25366
25481
  disconnect: "connection"
25367
25482
  },
25368
25483
  /** Reached as `vetta identity domain <command>` (`SUBGROUPS`). Org-level: no `--identity`. */
@@ -25384,10 +25499,12 @@ var FLAGS = {
25384
25499
  },
25385
25500
  /** Reached as `vetta identity connections <command>`. */
25386
25501
  connections: {
25502
+ apps: "search limit after",
25503
+ app: "app",
25387
25504
  list: "identity",
25388
25505
  show: "connection",
25389
25506
  configs: "",
25390
- "config-add": "connector auth scope client-id",
25507
+ "config-add": "connector auth scope tool client-id",
25391
25508
  "config-rm": "auth-config"
25392
25509
  },
25393
25510
  /** Reached as `vetta identity phone <command>` (`SUBGROUPS` in `cli.ts`). */
@@ -25472,7 +25589,12 @@ var FLAGS = {
25472
25589
  remove: ""
25473
25590
  },
25474
25591
  keys: { create: "name scopes", list: "limit after", rotate: "", revoke: "" },
25475
- credits: { show: "", topup: "usd", ledger: "type session agent deployment from to limit after" }
25592
+ credits: { show: "", topup: "usd", ledger: "type session agent deployment from to limit after" },
25593
+ /**
25594
+ * The monthly plan (§18b). No command takes a flag: the plan has one price, and neither
25595
+ * `subscribe` nor `portal` has anything to configure — each answers a URL for a human to open.
25596
+ */
25597
+ plan: { show: "", subscribe: "", portal: "" }
25476
25598
  };
25477
25599
  var GLOBAL_FLAGS = "json human profile api-key org api-base-url version help";
25478
25600
  var UNSUPPORTED = {
@@ -25495,7 +25617,7 @@ var ALIASES = {
25495
25617
  };
25496
25618
 
25497
25619
  // src/cli.ts
25498
- var VERSION = true ? "0.1.0" : "0.0.0-dev";
25620
+ var VERSION = true ? "0.3.0" : "0.0.0-dev";
25499
25621
  var GLOBAL_FLAGS_HELP = "global flags: --json (default) --human --profile <name> --api-key <key> --org <org_id> --api-base-url <url>";
25500
25622
  function usage() {
25501
25623
  const groups = [...Object.keys(GROUPS), ...Object.keys(ALIASES)].sort().join(", ");
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@usenaive-sdk/vetta-cli",
3
- "version": "0.1.0",
4
- "description": "Vetta command line \u2014 create agents, run sessions, watch events, see cost.",
3
+ "version": "0.3.0",
4
+ "description": "Vetta command line — create agents, run sessions, watch events, see cost.",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "bin": {
@@ -18,19 +18,18 @@
18
18
  "publishConfig": {
19
19
  "access": "public"
20
20
  },
21
+ "devDependencies": {
22
+ "@types/node": "22.20.1",
23
+ "esbuild": "0.28.2",
24
+ "yaml": "^2.9.0",
25
+ "@vetta/core": "0.0.0",
26
+ "@usenaive-sdk/vetta": "0.2.0"
27
+ },
21
28
  "scripts": {
22
29
  "build": "node build.mjs",
23
30
  "typecheck": "tsc --noEmit",
24
31
  "test": "vitest run",
25
32
  "test:local": "vitest run",
26
- "test:staging": "vitest run test/staging.e2e.ts",
27
- "prepublishOnly": "pnpm run build"
28
- },
29
- "devDependencies": {
30
- "@types/node": "catalog:",
31
- "esbuild": "0.28.2",
32
- "@vetta/core": "workspace:*",
33
- "@usenaive-sdk/vetta": "workspace:*",
34
- "yaml": "^2.9.0"
33
+ "test:staging": "vitest run test/staging.e2e.ts"
35
34
  }
36
- }
35
+ }