bkper 5.0.0 → 5.2.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 (75) hide show
  1. package/README.md +16 -0
  2. package/lib/agent/extensions/bkper-ai-provider.d.ts.map +1 -1
  3. package/lib/agent/extensions/bkper-ai-provider.js +47 -16
  4. package/lib/agent/extensions/bkper-ai-provider.js.map +1 -1
  5. package/lib/agent/extensions/builtins.d.ts.map +1 -1
  6. package/lib/agent/extensions/builtins.js +10 -2
  7. package/lib/agent/extensions/builtins.js.map +1 -1
  8. package/lib/agent/extensions/startup-logo.d.ts +9 -0
  9. package/lib/agent/extensions/startup-logo.d.ts.map +1 -0
  10. package/lib/agent/extensions/startup-logo.js +96 -0
  11. package/lib/agent/extensions/startup-logo.js.map +1 -0
  12. package/lib/agent/extensions/startup.d.ts.map +1 -1
  13. package/lib/agent/extensions/startup.js +63 -33
  14. package/lib/agent/extensions/startup.js.map +1 -1
  15. package/lib/agent/interactive/prompt-history-store.d.ts +1 -0
  16. package/lib/agent/interactive/prompt-history-store.d.ts.map +1 -1
  17. package/lib/agent/interactive/prompt-history-store.js +19 -5
  18. package/lib/agent/interactive/prompt-history-store.js.map +1 -1
  19. package/lib/agent/interactive/run-agent-mode.d.ts.map +1 -1
  20. package/lib/agent/interactive/run-agent-mode.js +5 -2
  21. package/lib/agent/interactive/run-agent-mode.js.map +1 -1
  22. package/lib/agent/interactive/settings.d.ts +26 -4
  23. package/lib/agent/interactive/settings.d.ts.map +1 -1
  24. package/lib/agent/interactive/settings.js +106 -16
  25. package/lib/agent/interactive/settings.js.map +1 -1
  26. package/lib/agent/interactive/themes.d.ts +13 -0
  27. package/lib/agent/interactive/themes.d.ts.map +1 -0
  28. package/lib/agent/interactive/themes.js +36 -0
  29. package/lib/agent/interactive/themes.js.map +1 -0
  30. package/lib/agent/startup-maintenance.d.ts +7 -4
  31. package/lib/agent/startup-maintenance.d.ts.map +1 -1
  32. package/lib/agent/startup-maintenance.js +13 -29
  33. package/lib/agent/startup-maintenance.js.map +1 -1
  34. package/lib/agent/system-prompt.d.ts.map +1 -1
  35. package/lib/agent/system-prompt.js +19 -7
  36. package/lib/agent/system-prompt.js.map +1 -1
  37. package/lib/agent/themes/bkper-dark.json +80 -0
  38. package/lib/agent/themes/bkper-light.json +80 -0
  39. package/lib/cli.js +6 -2
  40. package/lib/cli.js.map +1 -1
  41. package/lib/commands/agent-command.d.ts.map +1 -1
  42. package/lib/commands/agent-command.js +2 -1
  43. package/lib/commands/agent-command.js.map +1 -1
  44. package/lib/commands/upgrade.d.ts.map +1 -1
  45. package/lib/commands/upgrade.js +2 -3
  46. package/lib/commands/upgrade.js.map +1 -1
  47. package/lib/docs/ai/decision-models.md +316 -0
  48. package/lib/docs/apps/ai.md +120 -88
  49. package/lib/docs/apps/event-handlers.md +12 -0
  50. package/lib/docs/apps/overview.md +1 -1
  51. package/lib/docs/cli/app-management.md +1 -0
  52. package/lib/docs/index.md +2 -1
  53. package/lib/docs/sdk/bkper-api-types.md +4 -2
  54. package/lib/docs/sdk/bkper-js.md +1 -0
  55. package/lib/upgrade/index.d.ts +5 -4
  56. package/lib/upgrade/index.d.ts.map +1 -1
  57. package/lib/upgrade/index.js +3 -2
  58. package/lib/upgrade/index.js.map +1 -1
  59. package/lib/upgrade/installation.d.ts +54 -20
  60. package/lib/upgrade/installation.d.ts.map +1 -1
  61. package/lib/upgrade/installation.js +222 -122
  62. package/lib/upgrade/installation.js.map +1 -1
  63. package/lib/upgrade/update-check.d.ts +69 -0
  64. package/lib/upgrade/update-check.d.ts.map +1 -0
  65. package/lib/upgrade/update-check.js +253 -0
  66. package/lib/upgrade/update-check.js.map +1 -0
  67. package/lib/upgrade/update-worker.d.ts +2 -0
  68. package/lib/upgrade/update-worker.d.ts.map +1 -0
  69. package/lib/upgrade/update-worker.js +6 -0
  70. package/lib/upgrade/update-worker.js.map +1 -0
  71. package/lib/upgrade/upgrade.d.ts +11 -28
  72. package/lib/upgrade/upgrade.d.ts.map +1 -1
  73. package/lib/upgrade/upgrade.js +49 -96
  74. package/lib/upgrade/upgrade.js.map +1 -1
  75. package/package.json +6 -4
@@ -1,123 +1,155 @@
1
1
  # Add Bkper AI to an App
2
2
 
3
- Bkper Platform apps can call [Bkper AI](https://bkper.com/docs/ai/ai-gateway.md) without storing a model-provider key. The platform authorizes the Worker's request using the current user and attributes usage to the app. Model output is a suggestion, not permission to change a Book: keep decisions about resource movements and any resulting writes in application code.
3
+ Bkper Platform apps can call [Bkper AI](https://bkper.com/docs/ai/ai-gateway.md) without storing a model-provider key. The platform authorizes each request as the current user and attributes usage to the app. Scripts and servers outside the platform use the same code with their own Bkper token.
4
4
 
5
- This guide covers a server-side, non-streaming language response. It uses AI SDK to draft a review note from a transaction description. No Book data is written by the example.
5
+ Model output is a suggestion, not permission to change a Book. Keep decisions about resource movements, and any resulting writes, in application code.
6
6
 
7
7
  ## Choose the kind of response
8
8
 
9
- - **A bounded yes/no, choice, or score?** Use a typed evaluation at `POST /v1/evaluations`.
10
- - **Generated text or a custom JSON object?** Use a language model at `POST /v1/responses`. AI SDK is an option for this path.
9
+ - **A yes/no, a choice, or a level on a scale?** Ask a decision model, such as `jev`, with TypeSafe's SDK, `@typesafe-ai/sdk`.
10
+ - **Text or a custom JSON object?** Ask a language model (LLM), such as `gpt-luna`, with AI SDK's Open Responses provider, `@ai-sdk/open-responses`.
11
11
 
12
- Pick a model of the corresponding `type` from the live [`GET /v1/models` catalog](https://ai.bkper.app/v1/models). The catalog also tells you which language models support strict structured output. AI SDK's **Open Responses provider** covers language responses, not Bkper's typed evaluation endpoint. For evaluation models, use HTTP or implement an AI SDK evaluation-model adapter for `experimental_evaluate`, as `bkper-agent` does. See the [Typed Evaluations guide](https://bkper.com/docs/ai/evaluations.md) for requests, responses, decision thresholds, and errors, and the [Merge Duplicates app](https://github.com/bkper/bkper-apps/tree/main/merge-duplicates) for a human-reviewed HTTP example.
12
+ Add the SDK to the **Worker** package. Keep the call in a server service used by your authenticated app API route, and define the route's request and response in the app's Zod/OpenAPI contract.
13
13
 
14
14
  ## Keep authentication in the platform
15
15
 
16
16
  For an interactive app, the flow is:
17
17
 
18
- 1. The client calls a typed app `/api/*` route through `auth.authenticatedFetch()` (or another authenticated client). The template's generated API client can use that fetch provider.
18
+ 1. The client calls a typed app `/api/*` route through `auth.authenticatedFetch()` or another authenticated client.
19
19
  2. Bkper verifies the user's token, removes it before invoking the Worker, and establishes user and app context for outbound requests.
20
- 3. The Worker calls `https://ai.bkper.app/v1/*`. Platform outbound adds authorization and app attribution. **Do not read, forward, or store the user's token in the Worker.**
20
+ 3. The Worker calls Bkper AI. Platform outbound adds authorization and app attribution. **Do not read, forward, or store the user's token in the Worker.**
21
21
 
22
- An authenticated `/events` handler has the same outbound context. A page request does not; start interactive inference from an authenticated `/api/*` route, not from the page handler or the browser.
22
+ An authenticated `/events` handler has the same outbound context. A page request does not; start inference from an authenticated `/api/*` route, not from the page handler or the browser.
23
23
 
24
- ## Example: draft a review note with AI SDK
24
+ ## Ask a decision model
25
25
 
26
- Add `ai`, `@ai-sdk/open-responses`, and `zod` to the **Worker** package. Keep this code in a server service called from your authenticated app API route; define the route's request and response in the app's Zod/OpenAPI contract. The service takes only the description needed for this task, discovers the current language model, and validates its output before returning it to the caller.
26
+ ```ts
27
+ import { score, TypeSafeClient } from '@typesafe-ai/sdk';
28
+
29
+ const decisionClient = new TypeSafeClient({
30
+ apiKey: 'bkper-platform-outbound',
31
+ baseURL: 'https://ai.bkper.app',
32
+ defaultModel: 'jev',
33
+ });
34
+
35
+ export async function scoreRecurring(description: string) {
36
+ const { answers } = await decisionClient.systemOne({
37
+ state: { description },
38
+ questions: {
39
+ recurring: score('How likely is this a recurring charge?', [
40
+ 'Unlikely',
41
+ 'Possible',
42
+ 'Likely',
43
+ ]),
44
+ },
45
+ });
46
+ return answers.recurring;
47
+ }
48
+ ```
49
+
50
+ - **`apiKey`** is required by the SDK. In a Platform Worker, pass any placeholder: platform outbound replaces it with the user's authorization.
51
+ - **`baseURL`** has no `/v1`. The SDK calls `POST /v1/systemone`, which behaves exactly like `POST /v1/decisions`.
52
+ - **The answer is typed** from the question. Bkper AI validates every answer against its question before responding, so your code can use it directly.
53
+
54
+ The Decision Models guide covers questions, state, answers, and thresholds. The open-source [Merge Duplicates app](https://github.com/bkper/bkper-apps/tree/main/merge-duplicates) uses this setup to suggest duplicate pairs for human review.
55
+
56
+ ## Generate a language response
27
57
 
28
58
  ```ts
29
59
  import { createOpenResponses } from '@ai-sdk/open-responses';
30
60
  import { generateText, Output } from 'ai';
31
61
  import { z } from 'zod';
32
62
 
33
- const BASE_URL = 'https://ai.bkper.app/v1';
34
- const ReviewNote = z.strictObject({ note: z.string() });
35
-
36
- export async function draftReviewNote(
37
- description: string,
38
- fetcher: typeof fetch = fetch
39
- ): Promise<{ note: string }> {
40
- const response = await fetcher(`${BASE_URL}/models`);
41
- if (!response.ok) throw new Error(`Model discovery failed (${response.status}).`);
42
-
43
- const catalog = z
44
- .object({
45
- default_model: z.string(),
46
- data: z.array(
47
- z.object({
48
- id: z.string(),
49
- type: z.string(),
50
- structured_output: z
51
- .object({
52
- json_schema: z.boolean(),
53
- strict: z.boolean(),
54
- })
55
- .optional(),
56
- })
57
- ),
58
- })
59
- .parse(await response.json());
60
- const model = catalog.data.find(item => item.id === catalog.default_model);
61
- if (
62
- model?.type !== 'language' ||
63
- !model.structured_output?.json_schema ||
64
- !model.structured_output.strict
65
- ) {
66
- throw new Error('The default model does not support strict JSON output.');
67
- }
68
-
69
- const provider = createOpenResponses({
70
- name: 'bkper-ai',
71
- url: `${BASE_URL}/responses`,
72
- fetch: fetcher,
73
- });
63
+ const llmClient = createOpenResponses({
64
+ name: 'bkper-ai',
65
+ url: 'https://ai.bkper.app/v1/responses',
66
+ });
67
+
68
+ export async function draftReviewNote(description: string) {
74
69
  const { output } = await generateText({
75
- model: provider(model.id),
76
- system: 'Draft a short, neutral review note. Do not invent facts or change Accounts.',
70
+ model: llmClient('gpt-luna'),
71
+ system: 'Draft a short, neutral review note. Do not invent facts.',
77
72
  prompt: description,
78
- output: Output.object({ schema: ReviewNote }),
79
- maxRetries: 0,
73
+ output: Output.object({ schema: z.object({ note: z.string() }) }),
80
74
  });
81
- return ReviewNote.parse(output);
75
+ return output;
82
76
  }
83
77
  ```
84
78
 
85
- **This example is for a Bkper Platform Worker.** With no SDK `apiKey`, the Open Responses provider sends no `Authorization` header; platform outbound supplies authorization and app attribution. A standalone integration such as `bkper-agent` must instead obtain and send its own Bkper OAuth token. Open Responses always requests `strict: true` for structured JSON and omits `store`; Bkper AI treats an omitted `store` as `false` and never persists response state. If you need `strict: false` for a model-supported flexible schema, use `@ai-sdk/openai` with its `.responses()` model and `strictJsonSchema: false`, as `bkper-agent` does. You may cache the catalog briefly instead of fetching it for every call.
79
+ - **No `apiKey`.** The provider then sends no `Authorization` header, and platform outbound adds it.
80
+ - **`url`** is the full `/v1/responses` URL.
81
+ - **`output`** is parsed and validated against your schema by AI SDK. The provider requests strict structured output, which every Bkper language model supports.
86
82
 
87
- The schema deliberately checks only that a `note` string exists. Constraints such as a minimum or maximum string length are **not needed for this example** and may not be supported by every model's strict JSON Schema subset. If your app needs a length limit, check it in application code after generation.
88
-
89
- ## Before using the result
83
+ Keep schemas simple. Constraints such as a string's minimum or maximum length may not be supported by every model's strict JSON Schema subset; check them in code after generation.
90
84
 
91
- - Send only task-relevant data. Validate input and Book permissions at the app API boundary. A generated note must not create, merge, or alter a transaction without deterministic application rules and any required human confirmation.
92
- - If model discovery fails or output does not match the schema, fail safely. Avoid automatic retries on actions that may consume allowance.
93
- - When the AI request fails, preserve the upstream HTTP status, error code, and message **when Bkper AI supplies them**, so users can understand failures such as an exhausted allowance. AI SDK exposes HTTP failures as `APICallError`; read the Bkper AI error envelope from `responseBody`, not from the SDK's generic message. For `NoObjectGeneratedError` or transport errors, return a safe app-defined error instead. Do not return prompts, raw responses, or stack traces to clients.
94
- - Unit-test the server service with a mocked `fetch`: verify the model's type and capability, that no `Authorization` or attribution headers leave the Worker, that only necessary data is sent and `store: true` is never requested, that malformed output is rejected, and that errors remain actionable. Then run the app's normal check/build.
85
+ ## Outside a Platform app
95
86
 
96
- For example, a route can extract the safe fields before mapping them into its typed error response:
87
+ Scripts, servers, and tools send their own Bkper token. Pass it as `apiKey`: both SDKs send it as a bearer token. Get a client each time you need one, so every call uses a current token:
97
88
 
98
89
  ```ts
99
- import { APICallError } from 'ai';
100
-
101
- function bkperAiError(error: unknown) {
102
- if (!APICallError.isInstance(error)) return null;
103
- let body: unknown;
104
- try {
105
- body = JSON.parse(error.responseBody ?? '');
106
- } catch {
107
- return null;
108
- }
109
- const parsed = z
110
- .object({
111
- error: z.object({ code: z.string(), message: z.string() }),
112
- })
113
- .safeParse(body);
114
- if (!parsed.success) return null;
115
- return {
116
- status: error.statusCode ?? 502,
117
- code: parsed.data.error.code,
118
- message: parsed.data.error.message,
119
- };
120
- }
90
+ import { createOpenResponses } from '@ai-sdk/open-responses';
91
+ import { noul, TypeSafeClient } from '@typesafe-ai/sdk';
92
+ import { generateText } from 'ai';
93
+ import { getOAuthToken } from 'bkper';
94
+
95
+ const decisionClient = async () =>
96
+ new TypeSafeClient({
97
+ apiKey: await getOAuthToken(),
98
+ baseURL: 'https://ai.bkper.app',
99
+ defaultModel: 'jev',
100
+ });
101
+
102
+ const llmClient = async () =>
103
+ createOpenResponses({
104
+ name: 'bkper-ai',
105
+ url: 'https://ai.bkper.app/v1/responses',
106
+ apiKey: await getOAuthToken(),
107
+ });
108
+
109
+ const client = await decisionClient();
110
+ const { answers } = await client.systemOne({
111
+ state: { description: 'NETFLIX.COM monthly plan' },
112
+ questions: { streaming: noul('Is this a streaming service?') },
113
+ });
114
+
115
+ const { text } = await generateText({
116
+ model: (await llmClient())('gpt-luna'),
117
+ prompt: 'Describe NETFLIX.COM monthly plan in five words.',
118
+ });
121
119
  ```
122
120
 
123
- For direct HTTP calls, advanced features, or exact request and response fields, use the [Bkper AI API reference](https://bkper.com/docs/api/ai.md) and [AI Gateway guide](https://bkper.com/docs/ai/ai-gateway.md). Bkper AI implements a documented subset of Open Responses, not every OpenAI or AI SDK feature.
121
+ - **`getOAuthToken()`** reads the credentials from `bkper auth login` and refreshes them when needed. In your own server, use your own token provider.
122
+ - **Creating a client is cheap.** Neither SDK makes a request until you ask a question.
123
+ - **Label your usage** with a `bkper-ai-source` header, such as `my-script`: `defaultHeaders` in the TypeSafe SDK, `headers` in Open Responses. In a Platform app, outbound sets the source to the app.
124
+
125
+ ## Handle errors
126
+
127
+ Every Bkper AI error has the same envelope: `{ error: { message, type, param, code } }`. Branch on `error.code`, not only on the HTTP status: a `429` can mean an exhausted allowance or a throttled provider.
128
+
129
+ - **TypeSafe SDK:** an `APIError` with `status` and the envelope in `body`, so read `error.body.error.code`. Network failures and timeouts throw `APIConnectionError`.
130
+ - **AI SDK:** an `APICallError` with `statusCode` and the envelope in `data`, so read `error.data.error.code`. Output that does not match your schema throws `NoObjectGeneratedError`.
131
+
132
+ Both SDKs retry rate limits and server errors twice by default. A failed attempt costs nothing: Bkper AI charges only successful answers.
133
+
134
+ Map these errors to your route's typed error response. Keep the code and message so users understand failures such as an exhausted allowance, but never return prompts, raw responses, or stack traces.
135
+
136
+ ## SDK notes
137
+
138
+ - **Passing your own `fetch`.** The TypeSafe SDK calls `fetch` as its own method, which the Workers runtime rejects with `Illegal invocation`. Wrap it: `fetch: (input, init) => myFetch(input, init)`. Without the option, the SDK's default works.
139
+ - **No `null` values.** The TypeSafe SDK's types accept `null` for state, instructions, and some criteria. Bkper AI rejects them with `400`.
140
+ - **No `client.models.list()`.** It expects TypeSafe's catalog shape. Use the [`GET /v1/models`](https://ai.bkper.app/v1/models) catalog.
141
+ - **Other System One clients** take `https://ai.bkper.app/v1` as their base URL.
142
+ - **Flexible JSON schemas.** Open Responses always requests `strict: true`. For a schema that needs `strict: false`, such as a typed dynamic map, use `@ai-sdk/openai` with base URL `https://ai.bkper.app/v1`, its `.responses()` model, and `strictJsonSchema: false`.
143
+
144
+ ## Before using the result
145
+
146
+ - **Send only task-relevant data.** Validate input and Book permissions at the app API boundary.
147
+ - **Keep writes deterministic.** An answer or generated text must not create, merge, or alter a Transaction without application rules and any required human confirmation.
148
+ - **Test the service.** Mock `fetch` and check what is sent, that the Worker adds no user token, and that errors stay actionable. Then run the app's normal check and build.
149
+
150
+ ## Next steps
151
+
152
+ - [Decision Models](https://bkper.com/docs/ai/decision-models.md): questions, answers, thresholds, and caching.
153
+ - [Bkper AI Gateway](https://bkper.com/docs/ai/ai-gateway.md): tokens, raw HTTP requests, errors, and privacy.
154
+ - [Models and Usage](https://bkper.com/docs/ai/models.md): model IDs, capabilities, and usage rates.
155
+ - [Bkper AI API reference](https://bkper.com/docs/api/ai.md): every request and response field.
@@ -244,5 +244,17 @@ The complete API set of event types is listed below. `COMMENT_CREATED` and `COMM
244
244
  | `INTEGRATION_DELETED` | An integration was deleted. |
245
245
  | `BOOK_CREATED` | A book was created. |
246
246
  | `BOOK_AUDITED` | A balances audit completed for the book. |
247
+ | `BOOK_OVERNIGHT` | Daily scheduled event for the book, delivered at or after 01:00 in the book's time zone. |
247
248
  | `BOOK_UPDATED` | Book settings were updated. |
248
249
  | `BOOK_DELETED` | The book was deleted. |
250
+
251
+ ### Scheduled work with `BOOK_OVERNIGHT`
252
+
253
+ `BOOK_OVERNIGHT` lets your app run daily work on a book without waiting for a user action — for example, end-of-day recalculations, fetching external data, or consistency checks.
254
+
255
+ - **When:** once per day, at or after 01:00 in the book's time zone (UTC if the book has no time zone). Delivery may be up to about an hour later.
256
+ - **Which books:** only books where an installed app subscribes to `BOOK_OVERNIGHT`. Books without such an app receive nothing.
257
+ - **What day:** each event represents the local day that just ended. A book gets at most one `BOOK_OVERNIGHT` event per local day.
258
+ - **Idempotency:** the event `id` is stable for the book and day, so use it to skip repeated deliveries.
259
+ - **User:** the event `user` is the book owner. Your handler still runs on behalf of the user who installed the app.
260
+ - **Development webhook:** `webhookUrlDev` is used when the user who installed the app is its owner or one of its developers.
@@ -20,7 +20,7 @@ The same Worker can expose app-defined `/api/*` routes. Treat those routes as th
20
20
 
21
21
  When an app needs model inference, use Bkper AI by default. An authenticated app API route or event establishes the user and app identity, then platform outbound supplies authorization and usage attribution for the Worker's Bkper AI requests. The app does not need provider credentials.
22
22
 
23
- See [Add Bkper AI to an App](https://bkper.com/docs/platform/apps/ai.md) for live model discovery, strict structured output, validation, and the client-to-Worker authentication flow.
23
+ See [Add Bkper AI to an App](https://bkper.com/docs/platform/apps/ai.md) for the client-to-Worker authentication flow and SDK setup for decision and language models.
24
24
 
25
25
  ### Authentication
26
26
 
@@ -459,6 +459,7 @@ events:
459
459
  - BOOK_UPDATED
460
460
  - BOOK_DELETED
461
461
  - BOOK_AUDITED
462
+ - BOOK_OVERNIGHT # Daily per-Book event, at or after 01:00 in the Book's time zone
462
463
 
463
464
  # -----------------------------------------------------------------------------
464
465
  # FILE PATTERNS (optional)
package/lib/docs/index.md CHANGED
@@ -10,7 +10,8 @@ For Bkper app implementation, refactoring, or code review, always read `apps/qua
10
10
  - `cli/data-management.md` — CLI reference for managing financial data and files: books, accounts, groups, files, transactions, per-account balance queries, query operators (on:, after:, before:, account:, group:), JSON output shapes and jq reshaping, human-review Bkper UI links, batch operations via stdin/piping, collections.
11
11
  - `cli/app-management.md` — CLI reference for building and deploying Bkper apps: init/git clone/credential helpers, dev/build/deploy workflow, app install/uninstall, secrets management, app logs, bkper.yaml configuration reference (identity, branding, events, menu integration, deployment).
12
12
  - `apps/overview.md` — Platform evaluation and capability overview: use when comparing managed Bkper hosting with self-managed infrastructure or clarifying platform responsibilities; use the task-specific app references for implementation.
13
- - `apps/ai.md` — Bkper AI in Platform apps: choose Jev typed evaluations or Open Responses language models, authenticate through app `/api/*` routes, discover model capabilities, validate output, and keep Book writes under deterministic application control.
13
+ - `apps/ai.md` — Bkper AI in apps, scripts, and tools: ask the Jev decision model with TypeSafe's SDK or language models with AI SDK Open Responses, authenticate through app `/api/*` routes or a Bkper token outside the platform, handle errors, and keep Book writes under deterministic application control.
14
+ - `ai/decision-models.md` — Automating recurring judgments in code, such as categorizing bank lines, matching duplicates, or flagging entries, with Bkper AI decision models: writing questions and state, reading calibrated answers, and acting only above thresholds while routing uncertain cases to a person.
14
15
  - `apps/first-app.md` — First-app walkthrough: scaffold, install, run locally, trigger an event, customize the listing, establish shared source, check, and deploy.
15
16
  - `apps/architecture.md` — App and template architecture: npm workspace structure, Lit/Vite client, Hono Worker, typed `/api/*` contracts, authentication, `/events`, static assets, and supported app shapes.
16
17
  - `apps/quality.md` — Cross-cutting quality guidance for Bkper apps: UI consistency, immediate first rendering, typed API contracts, cohesive low-coupling modules, separation of business behavior from connectors and storage, security, and final implementation review. Load for app implementation, refactoring, or review tasks.
@@ -89,7 +89,7 @@ More information at the [Bkper Developer Documentation](https://bkper.com/docs/#
89
89
  - `deprecated?`: `boolean` — Whether the app is deprecated
90
90
  - `description?`: `string` — The App description
91
91
  - `developers?`: `string` — The developers (usernames and domain patterns), comma or space separated
92
- - `events?`: `("FILE_CREATED" | "FILE_UPDATED" | "FILE_DELETED" | "TRANSACTION_CREATED" | "TRANSACTION_UPDATED" | "TRANSACTION_DELETED" | "TRANSACTION_POSTED" | "TRANSACTION_CHECKED" | "TRANSACTION_UNCHECKED" | "TRANSACTION_RESTORED" | "ACCOUNT_CREATED" | "ACCOUNT_UPDATED" | "ACCOUNT_DELETED" | "QUERY_CREATED" | "QUERY_UPDATED" | "QUERY_DELETED" | "GROUP_CREATED" | "GROUP_UPDATED" | "GROUP_DELETED" | "COMMENT_CREATED" | "COMMENT_DELETED" | "COLLABORATOR_ADDED" | "COLLABORATOR_UPDATED" | "COLLABORATOR_REMOVED" | "INTEGRATION_CREATED" | "INTEGRATION_UPDATED" | "INTEGRATION_DELETED" | "BOOK_CREATED" | "BOOK_AUDITED" | "BOOK_UPDATED" | "BOOK_DELETED")[]` — Event types the App listen to
92
+ - `events?`: `("FILE_CREATED" | "FILE_UPDATED" | "FILE_DELETED" | "TRANSACTION_CREATED" | "TRANSACTION_UPDATED" | "TRANSACTION_DELETED" | "TRANSACTION_POSTED" | "TRANSACTION_CHECKED" | "TRANSACTION_UNCHECKED" | "TRANSACTION_RESTORED" | "ACCOUNT_CREATED" | "ACCOUNT_UPDATED" | "ACCOUNT_DELETED" | "QUERY_CREATED" | "QUERY_UPDATED" | "QUERY_DELETED" | "GROUP_CREATED" | "GROUP_UPDATED" | "GROUP_DELETED" | "COMMENT_CREATED" | "COMMENT_DELETED" | "COLLABORATOR_ADDED" | "COLLABORATOR_UPDATED" | "COLLABORATOR_REMOVED" | "INTEGRATION_CREATED" | "INTEGRATION_UPDATED" | "INTEGRATION_DELETED" | "BOOK_CREATED" | "BOOK_AUDITED" | "BOOK_OVERNIGHT" | "BOOK_UPDATED" | "BOOK_DELETED")[]` — Event types the App listen to
93
93
  - `filePatterns?`: `string[]` — File patterns the App handles - wildcard accepted. E.g. *.pdf, *-bank.csv
94
94
  - `id?`: `string` — The unique agent id of the App - this can't be changed after created
95
95
  - `installable?`: `boolean` — Whether this app is installable in a book
@@ -331,7 +331,7 @@ More information at the [Bkper Developer Documentation](https://bkper.com/docs/#
331
331
  - `data?`: `bkper.EventData`
332
332
  - `id?`: `string` — The unique id that identifies the Event
333
333
  - `resource?`: `string` — The resource associated to the Event
334
- - `type?`: `"FILE_CREATED" | "FILE_UPDATED" | "FILE_DELETED" | "TRANSACTION_CREATED" | "TRANSACTION_UPDATED" | "TRANSACTION_DELETED" | "TRANSACTION_POSTED" | "TRANSACTION_CHECKED" | "TRANSACTION_UNCHECKED" | "TRANSACTION_RESTORED" | "ACCOUNT_CREATED" | "ACCOUNT_UPDATED" | "ACCOUNT_DELETED" | "QUERY_CREATED" | "QUERY_UPDATED" | "QUERY_DELETED" | "GROUP_CREATED" | "GROUP_UPDATED" | "GROUP_DELETED" | "COMMENT_CREATED" | "COMMENT_DELETED" | "COLLABORATOR_ADDED" | "COLLABORATOR_UPDATED" | "COLLABORATOR_REMOVED" | "INTEGRATION_CREATED" | "INTEGRATION_UPDATED" | "INTEGRATION_DELETED" | "BOOK_CREATED" | "BOOK_AUDITED" | "BOOK_UPDATED" | "BOOK_DELETED"` — The type of the Event
334
+ - `type?`: `"FILE_CREATED" | "FILE_UPDATED" | "FILE_DELETED" | "TRANSACTION_CREATED" | "TRANSACTION_UPDATED" | "TRANSACTION_DELETED" | "TRANSACTION_POSTED" | "TRANSACTION_CHECKED" | "TRANSACTION_UNCHECKED" | "TRANSACTION_RESTORED" | "ACCOUNT_CREATED" | "ACCOUNT_UPDATED" | "ACCOUNT_DELETED" | "QUERY_CREATED" | "QUERY_UPDATED" | "QUERY_DELETED" | "GROUP_CREATED" | "GROUP_UPDATED" | "GROUP_DELETED" | "COMMENT_CREATED" | "COMMENT_DELETED" | "COLLABORATOR_ADDED" | "COLLABORATOR_UPDATED" | "COLLABORATOR_REMOVED" | "INTEGRATION_CREATED" | "INTEGRATION_UPDATED" | "INTEGRATION_DELETED" | "BOOK_CREATED" | "BOOK_AUDITED" | "BOOK_OVERNIGHT" | "BOOK_UPDATED" | "BOOK_DELETED"` — The type of the Event
335
335
  - `user?`: `bkper.User`
336
336
 
337
337
  ### EventData
@@ -547,6 +547,8 @@ More information at the [Bkper Developer Documentation](https://bkper.com/docs/#
547
547
  - `id?`: `string` — The user unique id
548
548
  - `name?`: `string` — The user display name
549
549
  - `plan?`: `string` — The user plan
550
+ - `planAmount?`: `number` — The confirmed recurring plan total in currency minor units
551
+ - `planCurrency?`: `string` — The confirmed recurring plan currency
550
552
  - `planCycle?`: `"MONTHLY" | "YEARLY"` — The user plan billing cycle
551
553
  - `planOverdue?`: `boolean` — True if subscription payment is overdue
552
554
  - `startedTrial?`: `boolean` — True if user started trial
@@ -1403,6 +1403,7 @@ Enum that represents event types.
1403
1403
  - `BOOK_AUDITED`
1404
1404
  - `BOOK_CREATED`
1405
1405
  - `BOOK_DELETED`
1406
+ - `BOOK_OVERNIGHT` — Daily per-Book event for Apps subscribed to it, delivered at or after 01:00 in the Book's time zone.
1406
1407
  - `BOOK_UPDATED`
1407
1408
  - `COLLABORATOR_ADDED`
1408
1409
  - `COLLABORATOR_REMOVED`
@@ -1,5 +1,6 @@
1
- export { VERSION, detectMethod, detectMethodAsync, fetchLatestVersion, getUpgradeCommand, isVersionInstalledAsync, startDetachedUpgrade, } from './installation.js';
2
- export { autoUpgrade, foregroundUpgrade, getAvailableUpgrade, isNewerVersion, } from './upgrade.js';
3
- export type { InstallMethod } from './installation.js';
4
- export type { AvailableUpgrade } from './upgrade.js';
1
+ export { PACKAGE_DIR, VERSION, detectInstallMethod, fetchLatestVersion, getInstallCommand, getSelfUpdatePlan, } from './installation.js';
2
+ export { foregroundUpgrade, isNewerVersion, runUpgrade } from './upgrade.js';
3
+ export { formatUpdateNotice, getUpdateNotice, isUpdateCheckDisabled, maybeStartUpdateCheck, readUpdateState, runCommandUpdateCheck, } from './update-check.js';
4
+ export type { InstallMethod, SelfUpdatePlan } from './installation.js';
5
+ export type { UpdateNotice } from './update-check.js';
5
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/upgrade/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,OAAO,EACP,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,uBAAuB,EACvB,oBAAoB,GACvB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACH,WAAW,EACX,iBAAiB,EACjB,mBAAmB,EACnB,cAAc,GACjB,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,YAAY,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/upgrade/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,WAAW,EACX,OAAO,EACP,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,GACpB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC7E,OAAO,EACH,kBAAkB,EAClB,eAAe,EACf,qBAAqB,EACrB,qBAAqB,EACrB,eAAe,EACf,qBAAqB,GACxB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACvE,YAAY,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC"}
@@ -1,3 +1,4 @@
1
- export { VERSION, detectMethod, detectMethodAsync, fetchLatestVersion, getUpgradeCommand, isVersionInstalledAsync, startDetachedUpgrade, } from './installation.js';
2
- export { autoUpgrade, foregroundUpgrade, getAvailableUpgrade, isNewerVersion, } from './upgrade.js';
1
+ export { PACKAGE_DIR, VERSION, detectInstallMethod, fetchLatestVersion, getInstallCommand, getSelfUpdatePlan, } from './installation.js';
2
+ export { foregroundUpgrade, isNewerVersion, runUpgrade } from './upgrade.js';
3
+ export { formatUpdateNotice, getUpdateNotice, isUpdateCheckDisabled, maybeStartUpdateCheck, readUpdateState, runCommandUpdateCheck, } from './update-check.js';
3
4
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/upgrade/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,OAAO,EACP,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,uBAAuB,EACvB,oBAAoB,GACvB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACH,WAAW,EACX,iBAAiB,EACjB,mBAAmB,EACnB,cAAc,GACjB,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/upgrade/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,WAAW,EACX,OAAO,EACP,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,GACpB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC7E,OAAO,EACH,kBAAkB,EAClB,eAAe,EACf,qBAAqB,EACrB,qBAAqB,EACrB,eAAe,EACf,qBAAqB,GACxB,MAAM,mBAAmB,CAAC"}
@@ -1,38 +1,72 @@
1
1
  /** Current installed version of the CLI. */
2
2
  export declare const VERSION: string;
3
+ /** Directory of the running bkper package (the folder holding its package.json). */
4
+ export declare const PACKAGE_DIR: string;
3
5
  /** Supported installation methods. */
4
- export type InstallMethod = 'npm' | 'bun' | 'yarn' | 'unknown';
5
- export type CommandRunner = (command: string, timeoutMs: number) => Promise<string>;
6
- export type DetachedCommandStarter = (command: string) => void;
6
+ export type InstallMethod = 'npm' | 'pnpm' | 'yarn' | 'bun' | 'unknown';
7
+ /** Where the running CLI lives and how it was started. */
8
+ export interface RuntimeLocation {
9
+ packageDir: string;
10
+ /** process.argv[1], not symlink-resolved. */
11
+ entrypoint?: string;
12
+ execPath: string;
13
+ isBunRuntime: boolean;
14
+ platform: NodeJS.Platform;
15
+ }
16
+ /** Side effects needed to decide whether the running copy can upgrade itself. */
17
+ export interface SelfUpdateEnvironment {
18
+ homedir: string;
19
+ readCommandOutput: (command: string) => string | undefined;
20
+ isWritable: (dir: string) => boolean;
21
+ }
22
+ export interface InstallPlan {
23
+ kind: 'install';
24
+ method: InstallMethod;
25
+ packageDir: string;
26
+ command: string;
27
+ /** package.json to read after installing, reached through the global root. */
28
+ packageJsonPath: string;
29
+ }
30
+ export interface ManualPlan {
31
+ kind: 'manual';
32
+ method: InstallMethod;
33
+ packageDir: string;
34
+ instruction: string;
35
+ }
36
+ export type SelfUpdatePlan = InstallPlan | ManualPlan;
37
+ export declare function getRuntimeLocation(): RuntimeLocation;
7
38
  /**
8
- * Detects how the CLI was installed by checking global package lists
9
- * for each supported package manager.
39
+ * Detects the package manager from the location of the running code
40
+ * (same rules as Pi's detectInstallMethod).
10
41
  */
11
- export declare function detectMethod(): InstallMethod;
42
+ export declare function detectInstallMethod(location?: RuntimeLocation): InstallMethod;
12
43
  /**
13
- * Async version of installation method detection to avoid blocking the event loop.
44
+ * Infers the npm prefix from a `<prefix>/lib/node_modules/bkper` layout.
45
+ * Windows prefixes look like project installs, so they are never inferred.
14
46
  */
15
- export declare function detectMethodAsync(commandRunner?: CommandRunner): Promise<InstallMethod>;
47
+ export declare function inferNpmPrefix(packageDir: string, platform: NodeJS.Platform): string | undefined;
16
48
  /**
17
- * Checks whether a specific Bkper version is already installed globally.
49
+ * Returns the shell command that installs a version with a package manager.
18
50
  */
19
- export declare function isVersionInstalledAsync(method: InstallMethod, version: string, commandRunner?: CommandRunner): Promise<boolean>;
51
+ export declare function getInstallCommand(method: InstallMethod, version: string, npmPrefix?: string): string | null;
20
52
  /**
21
- * Fetches the latest published version from the npm registry.
22
- * Returns null if the fetch fails.
53
+ * Decides whether the running copy can safely upgrade itself: it must be
54
+ * inside its package manager's global root and writable. Otherwise returns
55
+ * the manual instruction to show. Never suggests sudo.
23
56
  */
24
- export declare function fetchLatestVersion(): Promise<string | null>;
57
+ export declare function getSelfUpdatePlan(version: string, location?: RuntimeLocation, environment?: SelfUpdateEnvironment): SelfUpdatePlan;
25
58
  /**
26
- * Returns the shell command to upgrade the CLI for a given install method and version.
59
+ * Reads a package version from disk, bypassing the module cache.
27
60
  */
28
- export declare function getUpgradeCommand(method: InstallMethod, version: string): string | null;
61
+ export declare function readInstalledVersion(packageJsonPath: string): string | undefined;
29
62
  /**
30
- * Executes the upgrade to the specified version using the given install method.
31
- * Throws if the upgrade command fails.
63
+ * Runs an install command synchronously. BKPER_AUTOUPDATE_COMMAND replaces
64
+ * the command (for tests) but never the safety checks that lead here.
32
65
  */
33
- export declare function executeUpgrade(method: InstallMethod, version: string): void;
66
+ export declare function runInstallCommand(command: string): void;
34
67
  /**
35
- * Starts the upgrade in a detached background process.
68
+ * Fetches the latest published version from the npm registry.
69
+ * Returns null if the fetch fails.
36
70
  */
37
- export declare function startDetachedUpgrade(method: InstallMethod, version: string, commandStarter?: DetachedCommandStarter): void;
71
+ export declare function fetchLatestVersion(timeoutMs?: number): Promise<string | null>;
38
72
  //# sourceMappingURL=installation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"installation.d.ts","sourceRoot":"","sources":["../../src/upgrade/installation.ts"],"names":[],"mappings":"AAMA,4CAA4C;AAC5C,eAAO,MAAM,OAAO,EAAE,MAAoB,CAAC;AAK3C,sCAAsC;AACtC,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,SAAS,CAAC;AAE/D,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AACpF,MAAM,MAAM,sBAAsB,GAAG,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;AAwC/D;;;GAGG;AACH,wBAAgB,YAAY,IAAI,aAAa,CAkB5C;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACnC,aAAa,GAAE,aAAoC,GACpD,OAAO,CAAC,aAAa,CAAC,CAaxB;AAED;;GAEG;AACH,wBAAsB,uBAAuB,CACzC,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,MAAM,EACf,aAAa,GAAE,aAAoC,GACpD,OAAO,CAAC,OAAO,CAAC,CAoBlB;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAiBjE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAWvF;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAS3E;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAChC,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,MAAM,EACf,cAAc,GAAE,sBAAsD,GACvE,IAAI,CAUN"}
1
+ {"version":3,"file":"installation.d.ts","sourceRoot":"","sources":["../../src/upgrade/installation.ts"],"names":[],"mappings":"AAUA,4CAA4C;AAC5C,eAAO,MAAM,OAAO,EAAE,MAAoB,CAAC;AAK3C,oFAAoF;AACpF,eAAO,MAAM,WAAW,EAAE,MAGzB,CAAC;AAEF,sCAAsC;AACtC,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,SAAS,CAAC;AAExE,0DAA0D;AAC1D,MAAM,WAAW,eAAe;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,6CAA6C;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,OAAO,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC;CAC7B;AAED,iFAAiF;AACjF,MAAM,WAAW,qBAAqB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IAC3D,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;CACxC;AAED,MAAM,WAAW,WAAW;IACxB,IAAI,EAAE,SAAS,CAAC;IAChB,MAAM,EAAE,aAAa,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,8EAA8E;IAC9E,eAAe,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,UAAU;IACvB,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,EAAE,aAAa,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG,UAAU,CAAC;AAEtD,wBAAgB,kBAAkB,IAAI,eAAe,CAQpD;AAiCD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,GAAE,eAAsC,GAAG,aAAa,CAkBnG;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,GAAG,MAAM,GAAG,SAAS,CAWhG;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC7B,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,MAAM,EACf,SAAS,CAAC,EAAE,MAAM,GACnB,MAAM,GAAG,IAAI,CAgBf;AA+FD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC7B,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,eAAsC,EAChD,WAAW,GAAE,qBAAyD,GACvE,cAAc,CA6ChB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,eAAe,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAOhF;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAGvD;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,SAAS,SAAO,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAiBjF"}