@flovialabs/sdk 0.1.0 → 0.1.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.1
4
+
5
+ - CLI generation output shows the response status and incomplete reason. A delivered partial answer still exits 0; no continuation is generated automatically.
6
+ - CLI status shows bought balance; usage shows server-provided bought totals and the generation subtotal without adding them twice. Free usage stays in percentages.
7
+ - Corrected consent, replay lifetime, onboarding and copyable usage examples in the README.
8
+
3
9
  ## 0.1.0
4
10
 
5
11
  First release. Flovia is in alpha; APIs may change.
package/README.md CHANGED
@@ -10,11 +10,13 @@ TypeScript client and command-line tool for [Flovia](https://api.flovia.dev). It
10
10
  ## Install
11
11
 
12
12
  ```sh
13
- npm install @flovialabs/sdk
13
+ npm install --save-exact @flovialabs/sdk@0.1.1
14
14
  ```
15
15
 
16
16
  ## Your key
17
17
 
18
+ You need an invitation to [register and receive a Flovia key](https://api.flovia.dev/join). Keep the key private; it is shown once. Follow the [connection guide](https://api.flovia.dev/) to confirm your email and start the free allowance. Contact [Flovia support](https://flovia.dev/contact) if you need access or lose your key. Installing this package does not create an account or start an allowance.
19
+
18
20
  Set your Flovia key (`flv_...`) in the environment:
19
21
 
20
22
  ```sh
@@ -28,7 +30,7 @@ Or pass it as `new FloviaClient({ apiKey })`. The client keeps the key private:
28
30
  ## Quick start
29
31
 
30
32
  ```ts
31
- import { FloviaClient, FloviaError, newOperationId } from "@flovialabs/sdk";
33
+ import { FloviaClient, newOperationId } from "@flovialabs/sdk";
32
34
 
33
35
  const flovia = new FloviaClient(); // reads FLOVIA_KEY; base URL https://api.flovia.dev
34
36
 
@@ -37,7 +39,7 @@ const status = await flovia.status();
37
39
  console.log(status.five_hour.used_pct, status.week.used_pct);
38
40
 
39
41
  // Your lookups today (or since a time within the last 7 days)
40
- const usage = await flovia.usageToday({ since: "2026-10-10T00:00:00Z" });
42
+ const usage = await flovia.usageToday(); // today, UTC
41
43
 
42
44
  // Any tool: list them, then call one
43
45
  const tools = await flovia.listTools();
@@ -45,15 +47,36 @@ const reply = await flovia.call("flovia_status");
45
47
 
46
48
  // A text generation (uses credits). Save the operation id before sending.
47
49
  const op = newOperationId();
48
- const g = await flovia.generate({ model: "gpt-6-luna", input: "Summarize this." }, { operationId: op });
49
- console.log(g.outputText);
50
+ const request = { model: "gpt-6-luna" as const, input: "Explain a DEX aggregator in one sentence." };
51
+ // Persist { op, request } in your application's private storage before the next line.
52
+ const g = await flovia.generate(request, { operationId: op });
53
+ console.log(g.response.status, g.response.incomplete_details, g.outputText);
50
54
  ```
51
55
 
56
+ ### First data lookup
57
+
58
+ `listTools()` returns the tools offered to your account and their `inputSchema`. A catalog call sends nothing to the data provider. For example, list CoinGecko paths, then make one price lookup:
59
+
60
+ ```ts
61
+ await flovia.call("flovia_coingecko", {
62
+ path: "list", request_text: "Which prices can I read?", purpose: "choose a supported path",
63
+ });
64
+ const priceRequest = {
65
+ path: "/simple/price", query: { ids: "bitcoin", vs_currencies: "usd", include_last_updated_at: true },
66
+ request_text: "What is Bitcoin's USD price?", purpose: "check a price with its update time",
67
+ conversation_id: "sdk-price-example", operation_id: newOperationId(),
68
+ };
69
+ // Save priceRequest before sending; this lookup uses the shared allowance.
70
+ const price = await flovia.call("flovia_coingecko", priceRequest);
71
+ ```
72
+
73
+ If either call asks for a notice or consent, stop and follow the human-answer flow below. Read the returned source time, coverage and missing-value notes; a successful lookup is not a guarantee that it answers your question. Flovia exposes selected read-only paths, methods and public HTML hosts, not every feature of the upstream plans. Use the live catalog for the supported subset. OpenAI supports text generation with `gpt-6-luna`; it does not fetch other providers or offer image/audio generation.
74
+
52
75
  ### Operation ids and replay
53
76
 
54
77
  `generate()` requires an operation id (8-128 characters of `A-Z a-z 0-9 _ . : -`). Create one with `newOperationId()` and store it with the request **before** sending.
55
78
 
56
- If the reply is lost (timeout, network error, `executed: "unknown"`), send the same request again with the **same** id. Flovia returns the saved answer with `replayed: true` and does not charge again. Never resend under a new id: that may run and charge twice.
79
+ If the reply is lost (timeout, network error, `executed: "unknown"`), keep the request and **same** id. A completed or incomplete answer can normally be reopened for about **15 minutes** with the unchanged request, `replayed: true` and no additional charge. Follow `nextStep` for `operation_pending` or `upstream_unknown`; the answer may not yet be available. After `answer_expired` or `operation_retired`, Flovia cannot return the saved text. Never start a new id to recover a lost answer: that may run and charge twice. A genuinely new generation requires the person's decision to spend again.
57
80
 
58
81
  ## Errors
59
82
 
@@ -74,9 +97,9 @@ A refusal (`ok: false`) or a failed request throws `FloviaError`:
74
97
 
75
98
  These two codes are thrown on purpose. They need the person's own answer, and the SDK never answers for them.
76
99
 
77
- 1. Show `error.reply.user_notice` (or, for consent, `error.reply.consent.question`) to the person, unchanged.
78
- 2. Get the person's answer through your normal flow. For consent, send their own words to the `flovia_data_consent` tool.
79
- 3. Repeat the same call with the same arguments. After `notice_first`, add `notice_shown: true`.
100
+ 1. Show the full `error.reply.user_notice` unchanged. For `data_consent_needed`, also show `error.reply.consent.question`; the short question does not replace the notice.
101
+ 2. For `data_consent_needed`, get the person's own answer through your normal flow. Call `flovia_data_consent` with `answer` (`"y"` or `"n"` from that answer), `consent_version` from `error.reply.consent.version`, and `user_words` containing their actual words. Do not substitute the current SDK version or a generated answer. `notice_first` alone does not ask for a consent answer or supply a consent version.
102
+ 3. For `data_consent_needed`, repeat the unchanged call only after their `y` is accepted; with `n`, stop. For `notice_first`, repeat the same call after showing the notice, adding `notice_shown: true`.
80
103
 
81
104
  Do not answer these automatically from code or from an agent.
82
105
 
@@ -105,17 +128,20 @@ export FLOVIA_KEY=flv_... # or: --key-file ~/.config/flovia/key
105
128
  npx flovia status
106
129
  npx flovia usage --days 3
107
130
  npx flovia tools
108
- npx flovia call flovia_usage_today '{"since":"2026-10-11T00:00:00Z"}'
109
- npx flovia generate --new-op "Summarize this." # prints the operation id, then asks before sending
110
- npx flovia generate --op op_... --yes "Summarize this." # same id: the saved answer, not charged again
131
+ npx flovia tools --json # includes each tool's inputSchema
132
+ npx flovia call flovia_usage_today '{}'
133
+ npx flovia generate --op sdk-example-0001 --max-output 300 "Explain a DEX aggregator."
134
+ # Only to reopen that exact request within about 15 minutes (not a new generation):
135
+ npx flovia generate --op sdk-example-0001 --max-output 300 --yes "Explain a DEX aggregator."
111
136
  npx flovia help
112
137
  ```
113
- These commands run after installing `@flovialabs/sdk` in the current project. Without a local installation, specify the package: `npx --package=@flovialabs/sdk@0.1.0 flovia --help`.
138
+ These commands run after installing `@flovialabs/sdk` in the current project. Without a local installation, specify the package: `npx --package=@flovialabs/sdk@0.1.1 flovia --help`. Use a unique operation id for each new request; `sdk-example-0001` is only an example. Save the id, prompt and options before sending, and keep them unchanged for replay.
114
139
 
115
- `usage` shows lookup counts by source and the server-provided totals. It does not add rounded endpoint percentages. To continue a listing, copy the printed command with its exact `--since` and `--cursor`; do not combine `--days` with `--cursor`.
140
+ `status` shows bought balance. `usage` shows lookup counts, free percentages and server-provided bought totals; its generation subtotal is already included in the period total. Missing amounts show `-`, not zero. It does not add rounded endpoint percentages. To continue a listing, copy the printed command with its exact `--since` and `--cursor`; do not combine `--days` with `--cursor`.
116
141
 
117
142
  - Every command takes `--json` (the raw reply), `--base-url URL`, `--timeout SECONDS` and `--key-file PATH`.
118
143
  - `generate` uses credits. It needs `--op ID` or `--new-op`, and asks for confirmation on a terminal; add `--yes` in scripts.
144
+ - `generate` prints the response status and incomplete reason. `incomplete` / `max_output_tokens` means the delivered text stopped at the output limit, not that a continuation ran. Exit 0 means the response was delivered, not that the text is complete or correct. `--json` retains the full public response.
119
145
  - Exit codes: 0 ok, 1 an error from Flovia, 2 a usage error.
120
146
 
121
147
  ### Safety
@@ -123,7 +149,7 @@ These commands run after installing `@flovialabs/sdk` in the current project. Wi
123
149
  - The key is read only from `FLOVIA_KEY` or a key file, never from a command-line argument (other users can see arguments in `ps`). It is never printed.
124
150
  - A key file readable by other users gets a warning; use `chmod 600`.
125
151
  - `--base-url` must be `https`, so the key is never sent unencrypted. Plain `http` is allowed only for `localhost`, `127.0.0.1` and `[::1]`.
126
- - On `notice_first` or `data_consent_needed` the CLI shows the notice or question and stops. The person answers it themselves.
152
+ - On `notice_first` or `data_consent_needed` the CLI shows the full notice and, when present, the question, then stops. The person answers it themselves; `--yes` confirms spending, not data consent.
127
153
 
128
154
  ## License
129
155
 
package/dist/cli.js CHANGED
@@ -124,6 +124,7 @@ export function renderStatus(s, expected, now = Date.now()) {
124
124
  const lines = [`Flovia ${s.stage}, contract ${s.contract_version}${expected === null ? "" : ` (warning: this CLI expects ${expected})`}`];
125
125
  for (const [label, w] of [["5-hour window", s.five_hour], ["week", s.week]])
126
126
  lines.push(`${label.padEnd(14)} ${pct(w?.used_pct)} used${w?.resets_at ? `, resets ${when(w.resets_at, now)}` : label === "5-hour window" ? ", not started" : ""}`);
127
+ lines.push(`bought balance ${String(s["purchased_balance_display"] ?? "-")}`);
127
128
  const pending = obj(s["free_pending"]);
128
129
  if (pending["next_step"])
129
130
  lines.push(`free use not started: ${String(pending["next_step"])}`);
@@ -150,6 +151,7 @@ export function renderUsage(u, today = false, now = Date.now()) {
150
151
  const week = obj(u["free_week"]);
151
152
  lines.push(`free week: ${pct(week["used_pct"])} used${week["resets_at"] ? `, resets ${when(week["resets_at"], now)}` : ""}`);
152
153
  lines.push(`total in this period: ${String(obj(u["total"])["free_display"] ?? "-")} of the free week, ${String(u["count"] ?? 0)} lookups`);
154
+ lines.push(`bought credits used: ${String(obj(u["total"])["bought_display"] ?? "-")} total (includes generations)`);
153
155
  // Endpoint percentages are rounded (0 can mean <1%); only counts can be summed by source.
154
156
  const bySource = new Map();
155
157
  for (const e of Array.isArray(u["by_endpoint"]) ? u["by_endpoint"] : []) {
@@ -159,7 +161,7 @@ export function renderUsage(u, today = false, now = Date.now()) {
159
161
  if (bySource.size)
160
162
  lines.push("", table([["source", "lookups"], ...[...bySource].map(([n, count]) => [n, String(count)])]).trimEnd());
161
163
  const g = obj(u["generations"]);
162
- lines.push("", `generations: ${String(g["operations"] ?? 0)} (completed ${String(g["completed"] ?? 0)}, incomplete ${String(g["incomplete"] ?? 0)}, unknown ${String(g["unknown"] ?? 0)}), ${String(g["free_display"] ?? "-")} of the free week`);
164
+ lines.push("", `generations: ${String(g["operations"] ?? 0)} (completed ${String(g["completed"] ?? 0)}, incomplete ${String(g["incomplete"] ?? 0)}, unknown ${String(g["unknown"] ?? 0)}), ${String(g["free_display"] ?? "-")} of the free week, bought ${String(g["bought_display"] ?? "-")} (included in total)`);
163
165
  if (u.has_more)
164
166
  lines.push("", `more lookups: rerun flovia usage with the same --key-file and --base-url options, adding --since ${String(u["since"])} --cursor ${String(u.next_cursor)}`);
165
167
  return lines.join("\n") + "\n";
@@ -281,7 +283,7 @@ export async function run(argv, io) {
281
283
  // The id is shown before anything is sent, so a lost reply can be reopened with it (never resent under a new one).
282
284
  if (fresh)
283
285
  err(`operation_id: ${id}\nSave it. To resend or reopen this request, use: --op ${id}\n`);
284
- err("This generation uses Flovia credits (free windows first, then bought credits). Resending the same --op returns the saved answer at no charge.\n");
286
+ err("This generation uses Flovia credits (free windows first, then bought credits). The same --op and unchanged request can reopen a saved answer for about 15 minutes at no charge. Pending, unknown or expired answers must not be replaced with a new id.\n");
285
287
  if (!p.flags.has("--yes")) {
286
288
  if (io.confirm === null || text === "-")
287
289
  throw new Usage("generate: add --yes to send (no terminal to confirm on)");
@@ -291,10 +293,11 @@ export async function run(argv, io) {
291
293
  }
292
294
  }
293
295
  const g = await client.generate({ model: "gpt-6-luna", input, ...(max ? { max_output_tokens: max } : {}) }, { operationId: id });
296
+ const reason = obj(g.response["incomplete_details"])["reason"];
294
297
  if (json)
295
298
  print(g);
296
299
  else
297
- out(`${g.outputText}\n\n[operation_id ${id}${g.replayed ? ", replayed: the saved answer, not charged again" : ""}${g.generationId ? `, generation ${g.generationId}` : ""}]\n`);
300
+ out(`${g.outputText}\n\n[status: ${String(g.response["status"] ?? "unknown")}${reason ? `, reason: ${String(reason)}` : ""}, operation_id ${id}${g.replayed ? ", replayed: the saved answer, not charged again" : ""}${g.generationId ? `, generation ${g.generationId}` : ""}]\n`);
298
301
  return 0;
299
302
  }
300
303
  }
package/dist/client.d.ts CHANGED
@@ -35,7 +35,8 @@ export declare class FloviaClient {
35
35
  usageToday(args?: UsageTodayArgs): Promise<UsageTodayReply>;
36
36
  /**
37
37
  * POST /openai/v1/responses. operationId is required: save it with the request before calling. Sending the same request with the same id
38
- * again returns the saved answer (replayed: true) at no charge. Never resent automatically, and never under a new id.
38
+ * again can return the saved answer (replayed: true) for about 15 minutes at no charge. Pending, unknown and expired answers
39
+ * are not proof that nothing ran. Never resent automatically, and never under a new id to recover an answer.
39
40
  */
40
41
  generate(request: GenerationRequest, options: {
41
42
  operationId: string;
package/dist/client.js CHANGED
@@ -143,7 +143,8 @@ export class FloviaClient {
143
143
  }
144
144
  /**
145
145
  * POST /openai/v1/responses. operationId is required: save it with the request before calling. Sending the same request with the same id
146
- * again returns the saved answer (replayed: true) at no charge. Never resent automatically, and never under a new id.
146
+ * again can return the saved answer (replayed: true) for about 15 minutes at no charge. Pending, unknown and expired answers
147
+ * are not proof that nothing ran. Never resent automatically, and never under a new id to recover an answer.
147
148
  */
148
149
  async generate(request, options) {
149
150
  const op = options.operationId;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flovialabs/sdk",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "type": "module",
5
5
  "description": "TypeScript client and CLI for Flovia: MCP tools over HTTP and OpenAI-compatible generation. Zero dependencies.",
6
6
  "license": "MIT",