@loxtep/sdk 0.7.19 → 0.7.22

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 (38) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/README.md +12 -13
  3. package/dist/cli/commands/attach-cmd.d.ts.map +1 -1
  4. package/dist/cli/commands/attach-cmd.js +8 -3
  5. package/dist/cli/commands/attach-cmd.js.map +1 -1
  6. package/dist/cli/commands/config-cmd.d.ts.map +1 -1
  7. package/dist/cli/commands/config-cmd.js +3 -2
  8. package/dist/cli/commands/config-cmd.js.map +1 -1
  9. package/dist/cli/commands/ingest-cmd.d.ts.map +1 -1
  10. package/dist/cli/commands/ingest-cmd.js +1 -2
  11. package/dist/cli/commands/ingest-cmd.js.map +1 -1
  12. package/dist/cli/help.d.ts.map +1 -1
  13. package/dist/cli/help.js +0 -3
  14. package/dist/cli/help.js.map +1 -1
  15. package/dist/client/instances-types.d.ts +11 -0
  16. package/dist/client/instances-types.d.ts.map +1 -1
  17. package/dist/client/instances.d.ts +9 -13
  18. package/dist/client/instances.d.ts.map +1 -1
  19. package/dist/client/instances.js +6 -5
  20. package/dist/client/instances.js.map +1 -1
  21. package/dist/errors/parse-http.d.ts +0 -9
  22. package/dist/errors/parse-http.d.ts.map +1 -1
  23. package/dist/errors/parse-http.js +28 -4
  24. package/dist/errors/parse-http.js.map +1 -1
  25. package/dist/http/client.d.ts +8 -4
  26. package/dist/http/client.d.ts.map +1 -1
  27. package/dist/http/client.js +13 -13
  28. package/dist/http/client.js.map +1 -1
  29. package/dist/lib/instance-stream-config.d.ts +19 -3
  30. package/dist/lib/instance-stream-config.d.ts.map +1 -1
  31. package/dist/lib/instance-stream-config.js +94 -2
  32. package/dist/lib/instance-stream-config.js.map +1 -1
  33. package/docs/getting-started.md +4 -16
  34. package/docs/quick-reference.md +8 -9
  35. package/docs/sdk-first-ingest.md +53 -232
  36. package/package.json +1 -1
  37. package/docs/examples/generate-ingest-bundle.mjs +0 -133
  38. package/docs/examples/write-events.mjs +0 -54
@@ -1,263 +1,80 @@
1
- # SDK-first ingest your first data product and `get_writer`
1
+ # Write to a data product from your app
2
2
 
3
- You logged in, ran `init`, and `attach`. This guide takes you from there to
4
- **writing events from application code** with `client.get_writer(name)`.
3
+ You installed the SDK, initialized a workspace, and attached an instance. Next:
4
+ **create a data product**, open a writer, and send events from your code.
5
5
 
6
- That is the primary greenfield path when you are **not** wiring a SaaS connector
7
- first — you want your app (or ETL job) to push events into a **source** data
8
- product on your attached instance.
9
-
10
- > **Full CLI workspace setup:** [Getting Started](./getting-started.md) steps 1–4.
11
- > **Code-first workflow modules:** [Code-first CLI](./code-first-cli.md).
6
+ > Already set up login, init, and attach? [Getting started](./getting-started.md).
12
7
 
13
8
  ---
14
9
 
15
- ## What you are building
16
-
17
- ```text
18
- Your app ──get_writer──▶ rstreams queue ──▶ source data product (catalog)
19
- ▲ ▲
20
- │ │
21
- @loxtep/sdk provisioned at deploy time
22
- fromWorkspace()
23
- ```
10
+ ## Phase 1 Workspace ready
24
11
 
25
- `get_writer` resolves **queue**, **bot**, and **stream bus** config from
26
- **deployment metadata** (`deployment_bindings` on the data product). A catalog
27
- row alone is not enough — the ingestion workflow must be **saved and deployed**
28
- on your attached instance first.
29
-
30
- ---
31
-
32
- ## Prerequisites
33
-
34
- Run from your scaffolded workspace (directory with `.loxtep/project.json`):
12
+ Run commands from your project directory (the folder with `.loxtep/project.json`).
35
13
 
36
14
  ```bash
37
- pnpm exec loxtep config list # project_id + instance_id should be set
38
- pnpm exec loxtep domains list # you need a domain_id for the data product
15
+ pnpm exec loxtep login
16
+ pnpm exec loxtep init
17
+ pnpm exec loxtep attach --instance <instance-id>
18
+ pnpm exec loxtep config list
39
19
  ```
40
20
 
41
- Every org has at least one domain. Copy a `domain_id` from the list.
21
+ You should see `project_id` and `instance_id` from `attach`.
42
22
 
43
23
  ---
44
24
 
45
- ## Overview (four phases)
46
-
47
- | Phase | What | How |
48
- | ----- | ---- | --- |
49
- | 1 | Workspace ready | `login` → `init` → `attach` (done) |
50
- | 2 | Provision runtime | SDK connector + workflow bundle + deploy |
51
- | 3 | Verify | `data-products list` / `data-products get` |
52
- | 4 | Write events | `LoxtepClient.fromWorkspace()` + `get_writer` |
53
-
54
- Phases 2–4 below.
55
-
56
- ---
25
+ ## Phase 2 — Create your data product
57
26
 
58
- ## Phase 2 Provision runtime (required before `get_writer`)
59
-
60
- Runtime provisioning creates the workflow graph (SDK connection node → source
61
- data product node), saves it to your project workspace, and **deploys** it to your
62
- instance so queues and bots exist.
63
-
64
- ### Option A — CLI one-shot (recommended, no MCP)
65
-
66
- From your workspace root (after `login`, `init`, `attach`):
27
+ Pick a name your app will use (example: `app-events`):
67
28
 
68
29
  ```bash
69
30
  pnpm exec loxtep ingest provision --name app-events
70
31
  ```
71
32
 
72
- This command:
73
-
74
- 1. Creates an org-level **SDK connector** (`connector_type: "sdk"`).
75
- 2. Builds and saves the workflow bundle (`workflow.json` + connection + source data product).
76
- 3. **Deploys** to your attached instance (unless you pass `--no-deploy` or `--dry-run`).
77
-
78
- Validate first without writing:
79
-
80
- ```bash
81
- pnpm exec loxtep ingest provision --name app-events --dry-run
82
- ```
83
-
84
- ### Option B — Helper script + bundle save
85
-
86
- Generate bundle JSON only:
87
-
88
- ```bash
89
- node node_modules/@loxtep/sdk/docs/examples/generate-ingest-bundle.mjs
90
- ```
91
-
92
- Then persist and deploy via CLI:
93
-
94
- ```bash
95
- pnpm exec loxtep bundle save --dry-run --file .loxtep/sdk-ingest-bundle.json
96
- pnpm exec loxtep bundle save --file .loxtep/sdk-ingest-bundle.json
97
- pnpm exec loxtep workflows deploy \
98
- --project-id <project-id-from-config-list> \
99
- --instance-id <instance-id-from-config-list>
100
- ```
101
-
102
- Or set `LOXTEP_AUTO_SAVE=1` on the helper script to save + deploy in one step.
103
-
104
- Poll until deployment completes (Web UI **Observe**, or `loxtep observe status`).
105
-
106
- ### Option C — Loxtep MCP (Cursor / agent)
107
-
108
- Use **`loxtep_build`** → `save_workflow_bundle` if you prefer MCP authoring.
109
- Same bundle shape as below; deploy with `loxtep workflows deploy` or MCP
110
- `deploy_project`.
111
-
112
- ### Option D — Web UI (Studio)
113
-
114
- Create an **ingestion** workflow with an **SDK** connection node and a **source**
115
- data product node, then deploy to your attached instance from the Studio deploy
116
- flow.
117
-
118
- ---
119
-
120
- ### Workflow bundle shape
121
-
122
- Minimal ingestion bundle (SDK connection → source data product). Replace UUIDs
123
- and names; `connector_id` must be the SDK connector from step 2A or 2B.
124
-
125
- ```json
126
- {
127
- "workflow.json": {
128
- "workflow_id": "<workflow-uuid>",
129
- "organization_id": "<org-uuid>",
130
- "project_id": "<project-uuid>",
131
- "name": "SDK App Events Ingest",
132
- "workflow_type": "ingestion",
133
- "domain_id": "<domain-uuid>",
134
- "status": "active",
135
- "configuration": {},
136
- "metadata": {},
137
- "created_at": "2026-07-23T00:00:00.000Z",
138
- "updated_at": "2026-07-23T00:00:00.000Z"
139
- },
140
- "connections/<connection-uuid>.json": {
141
- "connection_id": "<connection-uuid>",
142
- "organization_id": "<org-uuid>",
143
- "project_id": "<project-uuid>",
144
- "workflow_id": "<workflow-uuid>",
145
- "connector_id": "<sdk-connector-uuid>",
146
- "key": "sdk-input",
147
- "name": "SDK Input",
148
- "type": "sdk",
149
- "status": "active",
150
- "configuration": {
151
- "sdk_type": "nodejs",
152
- "event_type": "app-events"
153
- },
154
- "created_at": "2026-07-23T00:00:00.000Z",
155
- "updated_at": "2026-07-23T00:00:00.000Z"
156
- },
157
- "data-products/<data-product-uuid>.json": {
158
- "data_product_id": "<data-product-uuid>",
159
- "organization_id": "<org-uuid>",
160
- "project_id": "<project-uuid>",
161
- "workflow_id": "<workflow-uuid>",
162
- "upstream_entity_id": "<connection-uuid>",
163
- "upstream_entity_type": "connections",
164
- "domain_id": "<domain-uuid>",
165
- "name": "app-events",
166
- "kind": "source",
167
- "status": "draft",
168
- "owner": {},
169
- "governance": {
170
- "classification": "internal",
171
- "pii_fields": [],
172
- "compliance_requirements": [],
173
- "tags": []
174
- },
175
- "metadata": {},
176
- "created_at": "2026-07-23T00:00:00.000Z",
177
- "updated_at": "2026-07-23T00:00:00.000Z"
178
- }
179
- }
180
- ```
181
-
182
- The **`name`** on the data product node (`app-events` above) is what you pass
183
- to `get_writer('app-events')`.
184
-
185
- ---
186
-
187
- ### Do not stop at `data-products create`
188
-
189
- ```bash
190
- # Creates a catalog row only — get_writer will fail with "not deployed"
191
- pnpm exec loxtep data-products create --name app-events --domain-id <uuid> --kind source
192
- ```
193
-
194
- Use **`data-products create`** only when you already have (or will immediately
195
- add) a deployed workflow that sets `deployment_bindings`. For greenfield SDK
196
- ingest, prefer the bundle + deploy path above.
33
+ That is all you need for SDK writes — one command, one name.
197
34
 
198
35
  ---
199
36
 
200
- ## Phase 3 — Verify deployment
37
+ ## Phase 3 — Confirm it exists
201
38
 
202
39
  ```bash
203
40
  pnpm exec loxtep data-products list
204
- pnpm exec loxtep data-products get <data-product-id>
205
41
  ```
206
42
 
207
- A deployable source data product includes **`deployment_bindings`** with
208
- `queue_name`, `bot_id`, and `instance_id`. If those are missing, finish Phase 2
209
- before writing.
210
-
211
- Re-run code generation after platform changes:
212
-
213
- ```bash
214
- pnpm exec loxtep generate
215
- ```
43
+ You should see `app-events` (or whatever name you chose).
216
44
 
217
45
  ---
218
46
 
219
- ## Phase 4 — Write events from your app
220
-
221
- Copy or run the example:
222
-
223
- ```bash
224
- node node_modules/@loxtep/sdk/docs/examples/write-events.mjs
225
- ```
47
+ ## Phase 4 — Write from your application
226
48
 
227
- Or embed in your service:
49
+ The SDK reads your workspace (`.loxtep/project.json` + credentials). No manual
50
+ URL or token wiring.
228
51
 
229
52
  ```typescript
230
53
  import { LoxtepClient } from '@loxtep/sdk';
231
54
 
232
55
  const client = await LoxtepClient.fromWorkspace();
233
56
 
234
- // Name from Phase 2 bundle / data-products list (case-sensitive)
235
57
  const writer = await client.get_writer('app-events');
236
58
 
237
59
  writer.write({
238
- event_id: 'evt_001',
239
- occurred_at: new Date().toISOString(),
240
- payload: { user_id: 'u_1', action: 'signup' },
60
+ user_id: 'u_1',
61
+ action: 'signup',
241
62
  });
242
63
 
243
- await writer.close(); // flush buffered events
64
+ await writer.close();
244
65
  ```
245
66
 
246
- Optional writer tuning:
67
+ Pass your **business fields** to `write()`. The SDK wraps them for transport —
68
+ you do not build an envelope or use a `payload` property yourself.
247
69
 
248
- ```typescript
249
- await client.get_writer('app-events', {
250
- batch_size: 500,
251
- max_retries: 5,
252
- });
253
- ```
70
+ Use the same name you passed to `ingest provision`.
254
71
 
255
- Read back (after data has landed):
72
+ ### Read back (optional)
256
73
 
257
74
  ```typescript
258
- const reader = await client.get_reader('app-events', {
259
- bot_id: 'my-app-reader',
260
- });
75
+ const client = await LoxtepClient.fromWorkspace();
76
+ const reader = await client.get_reader('app-events');
77
+
261
78
  for await (const event of reader) {
262
79
  console.log(event);
263
80
  break;
@@ -266,39 +83,43 @@ for await (const event of reader) {
266
83
 
267
84
  ---
268
85
 
269
- ## End-to-end checklist
86
+ ## Quick checklist
270
87
 
271
88
  ```bash
272
89
  pnpm exec loxtep login
273
90
  pnpm exec loxtep init
274
91
  pnpm exec loxtep attach --instance <instance-id>
275
- pnpm exec loxtep domains list
276
-
277
92
  pnpm exec loxtep ingest provision --name app-events
278
- # or: generate-ingest-bundle.mjs + loxtep bundle save
279
-
280
93
  pnpm exec loxtep data-products list
281
- node node_modules/@loxtep/sdk/docs/examples/write-events.mjs
282
94
  ```
283
95
 
96
+ Then `get_writer('app-events')` in your app.
97
+
284
98
  ---
285
99
 
286
- ## Troubleshooting
100
+ ## If something goes wrong
287
101
 
288
- | Symptom | Cause | Fix |
289
- | ------- | ----- | --- |
290
- | `StreamingError: is not deployed` | No `deployment_bindings` | Complete Phase 2 (bundle + deploy) |
291
- | `NotFoundError: Data product '…' not found` | Wrong name or wrong instance | `data-products list`; check `attach` / `instance_id` |
292
- | `AmbiguityError: Multiple data products match` | Same name on multiple instances | Use UUID or set `instance_id` in project.json |
293
- | Empty `domains list` | New org / permissions | Create a domain in the Web UI (Governance) |
102
+ | What you see | What to do |
103
+ | ------------ | ---------- |
104
+ | Data product not found | Run `data-products list`. Check the name matches `get_writer('…')` exactly. |
105
+ | Not ready to accept writes | Run `ingest provision` again for that name. Make sure you ran `attach` first. |
106
+ | Multiple products with the same name | Pass the data product id to `get_writer`, or ensure `attach` points at the right instance. |
107
+ | No domain available | Create a domain in the Loxtep UI, or pass `--domain-id` to `ingest provision`. |
294
108
 
295
109
  ---
296
110
 
297
- ## Next steps
111
+ ## Other ways to create a data product
112
+
113
+ **Loxtep MCP (AI assistant)** — If you use the Loxtep MCP in Cursor, Claude Code,
114
+ or another client, describe what you want in plain language, for example:
115
+
116
+ > Create a data product named `app-events` on my current Loxtep project and
117
+ > deploy it to the instance I already attached. I want to write events from my
118
+ > app with the SDK.
119
+
120
+ The assistant provisions the same end state as `ingest provision`. After that,
121
+ Phase 4 above is unchanged: `get_writer('app-events')` and `write()`.
298
122
 
299
- | Resource | Description |
300
- | -------- | ----------- |
301
- | [Getting Started](./getting-started.md) | Login, init, attach, generate |
302
- | [Quick Reference](./quick-reference.md) | CLI + SDK cheat sheet |
303
- | [Code-first CLI](./code-first-cli.md) | TypeScript workflow modules + `loxtep deploy` |
304
- | [Event Replay Cookbook](./event-replay-cookbook.md) | Replay and reprocess |
123
+ **Loxtep Studio (web app)** — *Beta.* You can also design and publish a data
124
+ product in the Loxtep UI. Once it exists on your instance, the SDK code in
125
+ Phase 4 is the same.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loxtep/sdk",
3
- "version": "0.7.19",
3
+ "version": "0.7.22",
4
4
  "description": "Loxtep SDK for Node.js — the Enterprise Context Layer: data products, workflows, projects, queues, and machine-usable context for AI",
5
5
  "author": "Loxtep <engineering@loxtep.io>",
6
6
  "homepage": "https://loxtep.io",
@@ -1,133 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Greenfield SDK ingest — one-shot provision via CLI (preferred) or bundle JSON for manual save.
4
- *
5
- * Run from your Loxtep workspace root (after login, init, attach):
6
- *
7
- * pnpm exec loxtep ingest provision --name app-events
8
- *
9
- * Or generate bundle JSON only:
10
- *
11
- * node node_modules/@loxtep/sdk/docs/examples/generate-ingest-bundle.mjs
12
- * pnpm exec loxtep bundle save --dry-run
13
- * pnpm exec loxtep bundle save
14
- */
15
-
16
- import { LoxtepClient, buildSdkIngestBundle } from '@loxtep/sdk';
17
- import { randomUUID } from 'node:crypto';
18
- import { writeFileSync, mkdirSync } from 'node:fs';
19
- import { join } from 'node:path';
20
-
21
- const DATA_PRODUCT_NAME = process.env.LOXTEP_DATA_PRODUCT_NAME ?? 'app-events';
22
- const WORKFLOW_NAME = process.env.LOXTEP_WORKFLOW_NAME ?? 'SDK App Events Ingest';
23
- const AUTO_SAVE = process.env.LOXTEP_AUTO_SAVE === '1';
24
-
25
- async function main() {
26
- const client = await LoxtepClient.fromWorkspace();
27
-
28
- const user = await client.session.get_current_user();
29
- const organizationId = user.organization_id ?? client.organization_id;
30
- const projectId = client.project_id;
31
- const instanceId = client.instance_id;
32
-
33
- if (!organizationId || !projectId) {
34
- console.error(
35
- 'Missing organization_id or project_id. Run from a workspace after `loxtep init`.'
36
- );
37
- process.exit(1);
38
- }
39
- if (!instanceId) {
40
- console.error('Missing instance_id. Run `loxtep attach --instance <id>` first.');
41
- process.exit(1);
42
- }
43
-
44
- let domainId = process.env.LOXTEP_DOMAIN_ID;
45
- if (!domainId) {
46
- const domains = await client.define.domains.list({ page_size: 20 });
47
- const items = domains.items ?? [];
48
- if (items.length === 0) {
49
- console.error(
50
- 'No domains found. Create one in the Web UI (Governance → Domains) or set LOXTEP_DOMAIN_ID.'
51
- );
52
- process.exit(1);
53
- }
54
- domainId = items[0].domain_id;
55
- console.error(`Using domain: ${items[0].name} (${domainId})`);
56
- }
57
-
58
- console.error('Creating SDK connector…');
59
- const connector = await client.connect.connectors.create({
60
- connector_type: 'sdk',
61
- metadata: { name: 'SDK Connector', created_by: 'generate-ingest-bundle' },
62
- });
63
-
64
- const bundle = buildSdkIngestBundle({
65
- organization_id: organizationId,
66
- project_id: projectId,
67
- domain_id: domainId,
68
- connector_id: connector.connector_id,
69
- data_product_name: DATA_PRODUCT_NAME,
70
- workflow_name: WORKFLOW_NAME,
71
- user_id: user.user_id,
72
- workflow_id: randomUUID(),
73
- connection_id: randomUUID(),
74
- data_product_id: randomUUID(),
75
- });
76
-
77
- mkdirSync('.loxtep', { recursive: true });
78
- const outPath = join('.loxtep', 'sdk-ingest-bundle.json');
79
- writeFileSync(outPath, JSON.stringify({ project_id: projectId, files: bundle.files }, null, 2));
80
-
81
- console.log(
82
- JSON.stringify(
83
- {
84
- connector_id: connector.connector_id,
85
- workflow_id: bundle.workflow_id,
86
- data_product_id: bundle.data_product_id,
87
- data_product_name: bundle.data_product_name,
88
- bundle_path: outPath,
89
- },
90
- null,
91
- 2
92
- )
93
- );
94
-
95
- if (AUTO_SAVE) {
96
- console.error('Saving workflow bundle (LOXTEP_AUTO_SAVE=1)…');
97
- const saveResult = await client.build.workflows.save_workflow_bundle(projectId, {
98
- files: bundle.files,
99
- dry_run: false,
100
- });
101
- console.error('Deploying…');
102
- const deployResult = await client.build.workflows.deploy({
103
- project_id: projectId,
104
- instance_id: instanceId,
105
- });
106
- console.log(JSON.stringify({ save: saveResult, deploy: deployResult }, null, 2));
107
- console.error(`
108
- Provisioned. Write events:
109
- LOXTEP_DATA_PRODUCT_NAME=${DATA_PRODUCT_NAME} node node_modules/@loxtep/sdk/docs/examples/write-events.mjs
110
- `);
111
- return;
112
- }
113
-
114
- console.error(`
115
- Next steps (no MCP required)
116
- ────────────────────────────
117
- Preferred — one command:
118
- pnpm exec loxtep ingest provision --name ${DATA_PRODUCT_NAME}
119
-
120
- Or save the generated bundle:
121
- pnpm exec loxtep bundle save --dry-run --file ${outPath}
122
- pnpm exec loxtep bundle save --file ${outPath}
123
- pnpm exec loxtep workflows deploy --project-id ${projectId} --instance-id ${instanceId}
124
-
125
- Write events:
126
- LOXTEP_DATA_PRODUCT_NAME=${DATA_PRODUCT_NAME} node node_modules/@loxtep/sdk/docs/examples/write-events.mjs
127
- `);
128
- }
129
-
130
- main().catch(err => {
131
- console.error(err instanceof Error ? err.message : err);
132
- process.exit(1);
133
- });
@@ -1,54 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Write sample events to a deployed source data product via get_writer.
4
- *
5
- * Prerequisites: login, init, attach, SDK ingest workflow deployed (see sdk-first-ingest.md).
6
- *
7
- * node node_modules/@loxtep/sdk/docs/examples/write-events.mjs
8
- *
9
- * Env:
10
- * LOXTEP_DATA_PRODUCT_NAME default: app-events
11
- */
12
-
13
- import { LoxtepClient } from '@loxtep/sdk';
14
-
15
- const DATA_PRODUCT_NAME = process.env.LOXTEP_DATA_PRODUCT_NAME ?? 'app-events';
16
-
17
- async function main() {
18
- const client = await LoxtepClient.fromWorkspace();
19
-
20
- console.error(`Opening writer for data product "${DATA_PRODUCT_NAME}"…`);
21
- const writer = await client.get_writer(DATA_PRODUCT_NAME);
22
-
23
- const events = [
24
- {
25
- event_id: `evt_${Date.now()}_1`,
26
- occurred_at: new Date().toISOString(),
27
- payload: { source: 'write-events.mjs', action: 'example_write', index: 1 },
28
- },
29
- {
30
- event_id: `evt_${Date.now()}_2`,
31
- occurred_at: new Date().toISOString(),
32
- payload: { source: 'write-events.mjs', action: 'example_write', index: 2 },
33
- },
34
- ];
35
-
36
- for (const event of events) {
37
- writer.write(event);
38
- console.log(JSON.stringify({ written: event.event_id }));
39
- }
40
-
41
- await writer.close();
42
- console.error('Writer closed — events flushed.');
43
- }
44
-
45
- main().catch(err => {
46
- const message = err instanceof Error ? err.message : String(err);
47
- console.error(message);
48
- if (/not deployed/i.test(message)) {
49
- console.error(
50
- '\nDeploy an SDK ingestion workflow first. See nodejs/docs/sdk-first-ingest.md'
51
- );
52
- }
53
- process.exit(1);
54
- });