@lotics/app-sdk 0.122.0 → 0.122.2
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/AGENTS.md +1 -1
- package/dist/{chunk-MK4DBY6R.js → chunk-I2BOTPL6.js} +4 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +6 -1
- package/dist/open_app.d.ts +12 -0
- package/dist/router.js +1 -1
- package/dist/rpc.d.ts +3 -1
- package/dist/viewer.d.ts +2 -0
- package/docs/members_and_options.md +1 -1
- package/docs/queries.md +1 -1
- package/docs/runtime.md +23 -2
- package/package.json +1 -1
package/AGENTS.md
CHANGED
|
@@ -24,7 +24,7 @@ This file is the index. The **exact type** of anything is its shipped declaratio
|
|
|
24
24
|
| [docs/navigation_and_state.md](./docs/navigation_and_state.md) | `AppRouter` (embedded/standalone URL model, the screens and `drawers` it reports to the host), `useUrlState` + `urlParam` codecs, `useRecents`, `useFolderPick`. |
|
|
25
25
|
| [docs/ai.md](./docs/ai.md) | `useAgentRun` (structured vs free-text, streaming parts, the agent's ask-back), `askAi`, `useAiContext`, and what the member's own chat agent can do with the app while it is open. |
|
|
26
26
|
| [docs/security.md](./docs/security.md) | **Read before shipping** — the owner-principal model, `is_current_member` scoping, write attribution, group gates, public-app bounds, why a per-input bound is a tenancy floor rather than an authorization check. |
|
|
27
|
-
| [docs/runtime.md](./docs/runtime.md) | `mount()` and the errors it reports to the host (`reportAppError`), the two transports, the API address a standalone bundle reads out of its own page (`<meta name="lotics-api-base">`), `rpc()`, the mock harness (`fixture` + `?__mock=1` — queries, workflows and agents, a mocked agent's run played line by line), `openExternal`/`openApp`/`downloadFile`, geofencing, the peer dependencies. |
|
|
27
|
+
| [docs/runtime.md](./docs/runtime.md) | `mount()` and the errors it reports to the host (`reportAppError`), the two transports, the API address a standalone bundle reads out of its own page (`<meta name="lotics-api-base">`), `rpc()`, the mock harness (`fixture` + `?__mock=1` — queries, workflows and agents, a mocked agent's run played line by line), `openExternal`/`openApp`/`peekApp`/`downloadFile`, geofencing, the peer dependencies. |
|
|
28
28
|
|
|
29
29
|
## Non-negotiables (each detailed in its doc)
|
|
30
30
|
|
|
@@ -1635,6 +1635,10 @@ function rpcStandalone(op, payload) {
|
|
|
1635
1635
|
return standalonePredictionMiss(payload);
|
|
1636
1636
|
case "openExternal":
|
|
1637
1637
|
return standaloneOpenExternal(payload);
|
|
1638
|
+
case "peekApp":
|
|
1639
|
+
return Promise.reject(
|
|
1640
|
+
new Error("peekApp needs the Lotics host: a standalone app has no sibling apps to open. Gate the control on isEmbedded().")
|
|
1641
|
+
);
|
|
1638
1642
|
case "openApp":
|
|
1639
1643
|
return Promise.reject(
|
|
1640
1644
|
new Error("openApp needs the Lotics host: a standalone app has no sibling apps to open. Gate the control on isEmbedded().")
|
package/dist/index.d.ts
CHANGED
|
@@ -24,7 +24,7 @@ export type { GeofenceZone, GeoCoords, GeofenceOutcome, GeofenceOptions } from "
|
|
|
24
24
|
export { rpc, isEmbedded } from "./rpc.js";
|
|
25
25
|
export type { RpcOp, AiContextValue, AiContextRecordRef } from "./rpc.js";
|
|
26
26
|
export { openExternal } from "./open_external.js";
|
|
27
|
-
export { openApp } from "./open_app.js";
|
|
27
|
+
export { openApp, peekApp } from "./open_app.js";
|
|
28
28
|
export { askAi, type AskAiArgs } from "./ask_ai.js";
|
|
29
29
|
export { downloadFile } from "./download.js";
|
|
30
30
|
export { renameFile } from "./rename_file.js";
|
package/dist/index.js
CHANGED
|
@@ -40,7 +40,7 @@ import {
|
|
|
40
40
|
sumDecimal,
|
|
41
41
|
tiersAscend,
|
|
42
42
|
urlParam
|
|
43
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-I2BOTPL6.js";
|
|
44
44
|
|
|
45
45
|
// src/mount.tsx
|
|
46
46
|
import { createRoot } from "react-dom/client";
|
|
@@ -29212,6 +29212,7 @@ function useAppContext() {
|
|
|
29212
29212
|
openableApps: data?.openable_apps ?? null,
|
|
29213
29213
|
commentsEnabled: data?.comments_enabled ?? false,
|
|
29214
29214
|
recordingEnabled: data?.recording_enabled === true,
|
|
29215
|
+
peekAppEnabled: data?.peek_app_enabled === true,
|
|
29215
29216
|
timezone: data?.timezone,
|
|
29216
29217
|
currency: data?.default_currency,
|
|
29217
29218
|
resolved: data !== void 0,
|
|
@@ -33031,6 +33032,9 @@ function openExternal(url2) {
|
|
|
33031
33032
|
function openApp(appId, route = "/") {
|
|
33032
33033
|
return rpc("openApp", { app_id: appId, route });
|
|
33033
33034
|
}
|
|
33035
|
+
function peekApp(appId, route = "/") {
|
|
33036
|
+
return rpc("peekApp", { app_id: appId, route });
|
|
33037
|
+
}
|
|
33034
33038
|
|
|
33035
33039
|
// src/ask_ai.ts
|
|
33036
33040
|
function askAi(args) {
|
|
@@ -33244,6 +33248,7 @@ export {
|
|
|
33244
33248
|
newRecordId,
|
|
33245
33249
|
openApp,
|
|
33246
33250
|
openExternal,
|
|
33251
|
+
peekApp,
|
|
33247
33252
|
queryAll,
|
|
33248
33253
|
readCreatedAt,
|
|
33249
33254
|
readFiles,
|
package/dist/open_app.d.ts
CHANGED
|
@@ -10,3 +10,15 @@
|
|
|
10
10
|
* Rejects standalone; gate on `isEmbedded()`.
|
|
11
11
|
*/
|
|
12
12
|
export declare function openApp(appId: string, route?: string): Promise<void>;
|
|
13
|
+
/**
|
|
14
|
+
* Open sibling app `appId`'s own `route` in a sheet over this app, which stays as it was beneath. The sibling draws its
|
|
15
|
+
* record itself, in its own frame, under its own workflows and permissions.
|
|
16
|
+
*
|
|
17
|
+
* ```tsx
|
|
18
|
+
* import { peekApp } from "@lotics/app-sdk";
|
|
19
|
+
* await peekApp(PARTIES_APP_ID, `/${party.id}`);
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* Rejects standalone, and on a host that does not state `useAppContext().peekAppEnabled`.
|
|
23
|
+
*/
|
|
24
|
+
export declare function peekApp(appId: string, route?: string): Promise<void>;
|
package/dist/router.js
CHANGED
package/dist/rpc.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ import { type UrlParams, type UrlParamsPatch } from "./url_params.js";
|
|
|
9
9
|
* app → host: { id, op, payload }
|
|
10
10
|
* host → app: { id, type: "result", data } | { id, type: "error", message }
|
|
11
11
|
*/
|
|
12
|
-
export type RpcOp = "query" | "export" | "field_options" | "workflow" | "agentRuns" | "agentRun.get" | "agentRun.cancel" | "upload" | "file.rename" | "members" | "context" | "write_model" | "prediction_miss" | "openExternal" | "openApp" | "askAi" | "urlState.get" | "urlState.set" | "folder.get" | "folder.set" | "comments.list" | "comments.create" | "comments.update" | "comments.delete" | "comments.counts" | "activity.list" | "activity.undo" | "recording.start" | "recording.stop";
|
|
12
|
+
export type RpcOp = "query" | "export" | "field_options" | "workflow" | "agentRuns" | "agentRun.get" | "agentRun.cancel" | "upload" | "file.rename" | "members" | "context" | "write_model" | "prediction_miss" | "openExternal" | "openApp" | "peekApp" | "askAi" | "urlState.get" | "urlState.set" | "folder.get" | "folder.set" | "comments.list" | "comments.create" | "comments.update" | "comments.delete" | "comments.counts" | "activity.list" | "activity.undo" | "recording.start" | "recording.stop";
|
|
13
13
|
export interface AgentRunPayload {
|
|
14
14
|
alias: string;
|
|
15
15
|
session_id: string;
|
|
@@ -72,6 +72,8 @@ export interface AppContext {
|
|
|
72
72
|
/** True only from a host with `recording.*`, for a signed-in member, on an
|
|
73
73
|
* instance that transcribes; absent reads as false. */
|
|
74
74
|
recording_enabled?: boolean;
|
|
75
|
+
/** True only from a host that serves `peekApp`; absent reads as false. */
|
|
76
|
+
peek_app_enabled?: boolean;
|
|
75
77
|
/** This app's recordings the host still tracks, by alias. */
|
|
76
78
|
recordings?: Record<string, unknown>;
|
|
77
79
|
/** Who in the organization is away, or will be, and who stands in — absent from an older host and for a visitor from
|
package/dist/viewer.d.ts
CHANGED
|
@@ -23,6 +23,8 @@ export declare function useAppContext(): {
|
|
|
23
23
|
}[] | null;
|
|
24
24
|
commentsEnabled: boolean;
|
|
25
25
|
recordingEnabled: boolean;
|
|
26
|
+
/** The host draws another app's record in a sheet over this one (`peekApp`). */
|
|
27
|
+
peekAppEnabled: boolean;
|
|
26
28
|
timezone: string | undefined;
|
|
27
29
|
currency: string | undefined;
|
|
28
30
|
resolved: boolean;
|
|
@@ -262,7 +262,7 @@ context, read once as the app opens. Display only: the workflow body re-checks w
|
|
|
262
262
|
(`current_member_stands_in_for(...)`).
|
|
263
263
|
|
|
264
264
|
`openableApps` lists the workspace's apps the viewer can open, each by its `id` and its `name` — what a
|
|
265
|
-
way through to a sibling app (`openApp`) is offered by. `null` where the host did not say (standalone, a
|
|
265
|
+
way through to a sibling app (`openApp`, `peekApp`) is offered by. `null` where the host did not say (standalone, a
|
|
266
266
|
render, an older host, or a page that had not listed its apps when the app opened). Display only: the
|
|
267
267
|
app opened checks the viewer itself.
|
|
268
268
|
|
package/docs/queries.md
CHANGED
|
@@ -142,7 +142,7 @@ Delivery-layer enrichment (applied to the response, per request):
|
|
|
142
142
|
(`readSelect(cell).map((o) => o.key)`) against any alias that projects the underlying field
|
|
143
143
|
DIRECTLY.
|
|
144
144
|
- **`number` columns** arrive as JS numbers (Postgres `numeric` strings are coerced; a
|
|
145
|
-
|
|
145
|
+
decimal with more digits than a double holds rounds to the double). **`date`/`datetime`** columns
|
|
146
146
|
arrive as canonical wall-clock strings (`YYYY-MM-DD` / `YYYY-MM-DDTHH:mm`).
|
|
147
147
|
|
|
148
148
|
### System columns
|
package/docs/runtime.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
How an app boots and talks to the platform: **`mount()`** (with the design-time mock harness),
|
|
4
4
|
the **two transports** the SDK switches between (app code never branches), the raw **`rpc()`**
|
|
5
5
|
escape hatch, and the browser capabilities the sandbox would otherwise block — **`openExternal`**,
|
|
6
|
-
**`openApp`**, **`downloadFile`**, and **`requestGeofencedLocation`** / **`isWithinZone`**.
|
|
6
|
+
**`openApp`** / **`peekApp`**, **`downloadFile`**, and **`requestGeofencedLocation`** / **`isWithinZone`**.
|
|
7
7
|
|
|
8
8
|
## `mount()` — the entry point
|
|
9
9
|
|
|
@@ -216,6 +216,7 @@ surfaces:
|
|
|
216
216
|
| `agentRuns` (session history) | **no** — the product host doesn't implement it either (`"Unknown RPC op: agentRuns"`) | rejects — a standalone caller is anonymous, and a history is its member's (`"App agent runs require an authenticated member …"`) |
|
|
217
217
|
| `askAi` | yes | rejects — `"askAi is only available when the app runs inside Lotics"` |
|
|
218
218
|
| `openApp` | yes — routes to the sibling in the same tab | rejects — `"openApp needs the Lotics host …"` |
|
|
219
|
+
| `peekApp` | yes, when the context states `peek_app_enabled` — opens the sibling in a sheet over this app | rejects — `"peekApp needs the Lotics host …"` |
|
|
219
220
|
| `recording.start`, `recording.stop` | yes, when the context states `recording_enabled` | rejects — `"Recording needs the Lotics host and a signed-in member …"` |
|
|
220
221
|
|
|
221
222
|
**Limitation:** Don't build a session-log UI on `useAgentRuns`; keep the log in app
|
|
@@ -257,6 +258,7 @@ semantics are documented:
|
|
|
257
258
|
| `context` | `{}` | app identity (below) | this doc |
|
|
258
259
|
| `openExternal` | `{ url }` | `void` | this doc |
|
|
259
260
|
| `openApp` | `{ app_id, route }` | `void` | this doc |
|
|
261
|
+
| `peekApp` | `{ app_id, route }` | `void` | this doc |
|
|
260
262
|
| `askAi` | prompt/files/records seed | `void` | [ai](./ai.md) |
|
|
261
263
|
| `agentRuns` | `{ session_id, limit?, offset? }` | `{ runs }` | [ai](./ai.md) |
|
|
262
264
|
| `agentRun.get` | `{ run_id }` | `{ run }` | [ai](./ai.md) |
|
|
@@ -269,7 +271,7 @@ The **streaming** agent-run op is *not* reachable through `rpc()` — its
|
|
|
269
271
|
response is a chunk stream, not a single value; it's internal to `useAgentRun`.
|
|
270
272
|
|
|
271
273
|
`rpc("context", {})` resolves the viewer and the workspace:
|
|
272
|
-
`{ member_id, comments_enabled, timezone, default_currency, recording_enabled?, recordings? }`. `member_id` is the
|
|
274
|
+
`{ member_id, comments_enabled, timezone, default_currency, recording_enabled?, peek_app_enabled?, recordings? }`. `member_id` is the
|
|
273
275
|
signed-in member when embedded, `null` standalone; `timezone` is the workspace's
|
|
274
276
|
IANA zone and `default_currency` its ISO 4217 code, from the host when embedded
|
|
275
277
|
and from `/v1/apps/by-subdomain/{subdomain}` standalone. Read them through
|
|
@@ -341,6 +343,25 @@ declares it (`/` for its register); it is the target's to define.
|
|
|
341
343
|
to go. A hop to the app's own id is refused — navigate inside an app with
|
|
342
344
|
its own router.
|
|
343
345
|
|
|
346
|
+
## `peekApp()` — a sibling's record, over this app
|
|
347
|
+
|
|
348
|
+
```tsx
|
|
349
|
+
import { peekApp, useAppContext } from "@lotics/app-sdk";
|
|
350
|
+
const { peekAppEnabled } = useAppContext();
|
|
351
|
+
if (peekAppEnabled) await peekApp(PARTIES_APP_ID, `/${party.id}`);
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
`peekApp(appId: string, route?: string): Promise<void>` (`dist/open_app.d.ts`). The host opens `route` of `appId` in
|
|
355
|
+
a side sheet over this app — a panel from the right on a desk, a full-height sheet on a phone — and this app stays as
|
|
356
|
+
it was beneath. The sibling draws its own record, in its own frame, under its own workflows and permissions; this app
|
|
357
|
+
never draws a second copy of it. The sheet's head carries the sibling's name, "Open in <name>" (what `openApp` does,
|
|
358
|
+
closing the sheet) and a close; Escape closes it too. One sheet at a time: a `peekApp` from inside the sheet replaces it.
|
|
359
|
+
|
|
360
|
+
- **Checked at the bridge**, as `openApp`'s target: the id by shape, the route as an in-app path. A hop to the app's own
|
|
361
|
+
id, and to an app the host's `openableApps` does not list, is refused.
|
|
362
|
+
- **By transport**: embedded in the product host opens the sheet; standalone rejects (`peekApp needs the Lotics host …`).
|
|
363
|
+
A host that does not serve the op states no `peekAppEnabled` in `context` — gate the control on it, else offer `openApp`.
|
|
364
|
+
|
|
344
365
|
## `downloadFile()` — save browser-built bytes
|
|
345
366
|
|
|
346
367
|
```tsx
|
package/package.json
CHANGED