streamby-core 0.41.0 → 0.43.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.
- package/CHANGELOG.md +89 -0
- package/README.md +143 -8
- package/dist/adapters/database/connectionManager.js +33 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.js +5 -1
- package/dist/mcp/tools.js +93 -11
- package/dist/mcp/workflowNodes.js +9 -3
- package/dist/middleware/createRouter.js +6 -0
- package/dist/middleware/routes/connection.js +5 -2
- package/dist/middleware/routes/dbConnection.js +29 -5
- package/dist/middleware/routes/document.d.ts +3 -0
- package/dist/middleware/routes/document.js +348 -0
- package/dist/middleware/routes/export.d.ts +28 -0
- package/dist/middleware/routes/export.js +8 -4
- package/dist/middleware/routes/pipeline.js +5 -2
- package/dist/middleware/routes/project.js +17 -12
- package/dist/middleware/routes/projectResources.d.ts +8 -0
- package/dist/middleware/routes/projectResources.js +51 -0
- package/dist/middleware/routes/rights.js +72 -52
- package/dist/middleware/routes/storage.js +35 -2
- package/dist/middleware/routes/storageConnection.js +38 -15
- package/dist/middleware/routes/userIntegration.js +42 -1
- package/dist/middleware/routes/workflow.js +120 -37
- package/dist/models/Model.js +12 -1
- package/dist/services/apiTokenPermissions.d.ts +1 -1
- package/dist/services/apiTokenPermissions.js +10 -0
- package/dist/services/auth.d.ts +2 -0
- package/dist/services/auth.js +17 -1
- package/dist/services/creditsSheets.d.ts +39 -0
- package/dist/services/creditsSheets.js +129 -0
- package/dist/services/dependents.d.ts +11 -0
- package/dist/services/dependents.js +81 -0
- package/dist/services/documents.d.ts +66 -0
- package/dist/services/documents.js +347 -0
- package/dist/services/executor.js +8 -0
- package/dist/services/oauthTokens.js +2 -2
- package/dist/services/production.d.ts +1 -1
- package/dist/services/production.js +2 -2
- package/dist/services/projectIntegration.d.ts +14 -0
- package/dist/services/projectIntegration.js +32 -0
- package/dist/services/rights.d.ts +44 -0
- package/dist/services/rights.js +123 -0
- package/dist/services/workflowResources.d.ts +90 -0
- package/dist/services/workflowResources.js +288 -0
- package/dist/types/index.d.ts +45 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,94 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.43.0
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **License metadata of assets**, for crediting the ones taken from others: `sourceUrl`,
|
|
8
|
+
`creatorName`, `creatorUrl`, `licenseType` and `comments`, all optional, in `asset_rights`.
|
|
9
|
+
The license is one of a closed list (`LICENSE_TYPES`: CC BY, CC BY-SA, CC BY-ND, CC BY-NC,
|
|
10
|
+
CC BY-NC-SA, CC BY-NC-ND, CC0, Free Standard, Standard, Editorial), checked by the server; the
|
|
11
|
+
links are http(s) URLs. It can be given with an upload (query params of `GET .../upload-url`;
|
|
12
|
+
an API token needs `rights:write` too). An `assetRightsEvent` to the project's active members
|
|
13
|
+
on each change, and when an asset is deleted.
|
|
14
|
+
- **Credits sheets**: project documents of a new kind, `credits`, that a workflow's Credits sheet
|
|
15
|
+
node makes (`CREDITS_SHEET_NODE`). One keeps only its sources (assets and Storage folders picked,
|
|
16
|
+
and the Ingest nodes connected to its node); its rows are made when it's read, from each asset's
|
|
17
|
+
metadata, with its status: complete when it has an asset and each has its creator, source and
|
|
18
|
+
license. Routes `GET /projects/:projectId/credits-sheets` and `.../credits-sheets/:docId`
|
|
19
|
+
(`rights:read`). Types `CreditsSheet`, `SheetRow`, `SheetSources` and `SheetStatus`.
|
|
20
|
+
- MCP: `get_credits_sheet`; `request_asset_upload` takes the license metadata.
|
|
21
|
+
- **The workflow defines the project.** Each resource is a node of it, bound by the resource's
|
|
22
|
+
id in its data. A resource added or removed from its section (`POST`/`DELETE` of a connection,
|
|
23
|
+
an API, an export, a pipeline, a project integration) adds or removes its node; saving the
|
|
24
|
+
workflow connects what its nodes were set to (`"__connect__:<integrationId>"`), makes the APIs
|
|
25
|
+
drafted on them (`data.connectionDraft`) and removes the resources whose nodes are gone,
|
|
26
|
+
answering `{ data, project, errors }`. A save needs `baseUpdatedAt` (a 409 over a newer version)
|
|
27
|
+
and a `nodeSchema` with `nodes` and `edges` lists (a 400 otherwise); an app connected with OAuth
|
|
28
|
+
can't remove resources through it (a 403 listing them). Pipelines and exports drafted on nodes
|
|
29
|
+
are made on save too. A node with a configuration of its own (a data source's table, an
|
|
30
|
+
Ingest's file) consumes its resource and is never merged into its node.
|
|
31
|
+
A workflow made before this is brought to it once, when it's read (`resourceNodesAt`).
|
|
32
|
+
- **What depends on a resource**, asked before removing it: `GET /projects/:id/dependents/:kind/:resourceId`
|
|
33
|
+
(connections, exports, pipelines): the workflow's, exports' and pipelines' nodes that use it,
|
|
34
|
+
and the files, folders, deliverables and shots kept for it. `GET /user/integrations/:id/dependents`:
|
|
35
|
+
the project connections that use an account's integration.
|
|
36
|
+
- **A change to a project's resources answers its list as it is then**, for the client to show
|
|
37
|
+
without asking again: connecting or disconnecting a database (`dbConnections`), a storage
|
|
38
|
+
(`storageConnections`, with built-ins' `available`) or an API (`connections`), deleting an
|
|
39
|
+
export (`exports`) or a pipeline (`pipelines`); `POST`/`DELETE /projects/:id/integrations`
|
|
40
|
+
answer the `project`; deleting an account's integration, each project it was disconnected
|
|
41
|
+
from (`affected`).
|
|
42
|
+
- **A built-in connected again keeps the id it had** (`disconnectedBuiltins` on the project; a
|
|
43
|
+
new `disconnectedBuiltins` column in a PostgreSQL projects table, added at startup), so its
|
|
44
|
+
files and records are its own again.
|
|
45
|
+
|
|
46
|
+
### Changed
|
|
47
|
+
|
|
48
|
+
- **`asset_rights` holds the license metadata only.** `rightsHolder`, `licenseUrl`, `territory`,
|
|
49
|
+
`expiresAt`, `usageRestrictions` and `notes` are gone: not read, and dropped from a record when
|
|
50
|
+
it's next saved; a `licenseType` off the list reads as none. `set_asset_rights` and
|
|
51
|
+
`get_asset_rights` take and give the new fields.
|
|
52
|
+
|
|
53
|
+
### Fixed
|
|
54
|
+
|
|
55
|
+
- Deleting an asset or a project deletes its license metadata (it was left behind).
|
|
56
|
+
- A built-in database or storage can be disconnected from a project (it was refused with a 403,
|
|
57
|
+
though the backfill already left a disconnected one alone): what the project keeps in it stays.
|
|
58
|
+
- Deleting an account's integration disconnects the project connections that use it (they were
|
|
59
|
+
left pointing at nothing).
|
|
60
|
+
- `POST .../connections/db` and `.../connections/storage` with an `integrationId` connect a
|
|
61
|
+
built-in too (they refused it as an unknown integration, and checked the pool's `sql`/`nosql`
|
|
62
|
+
as the type): with an integration, its name and type are the integration's.
|
|
63
|
+
|
|
64
|
+
## 0.42.0
|
|
65
|
+
|
|
66
|
+
### Added
|
|
67
|
+
|
|
68
|
+
- **Project documents**: markdown a project's team reads and edits in the UI, and agents read and
|
|
69
|
+
keep up to date through the MCP server, with every save a version (who saved it, and when). The
|
|
70
|
+
README's Documents section documents them.
|
|
71
|
+
- **New tables: `documents` and `document_versions`.** They're created at startup in the main
|
|
72
|
+
database (collections in MongoDB; in memory without one).
|
|
73
|
+
- Routes under `/projects/:projectId/documents`: the tree, a document, creating, a new version
|
|
74
|
+
(`PATCH` with `baseVersion`; 409 with the document as it is now when it changed since), a
|
|
75
|
+
section's (`PATCH .../sections`), moving and marking exportable, the history
|
|
76
|
+
(`.../versions[/:version]`), publishing a version (`POST .../publish`) and deleting (a folder
|
|
77
|
+
with everything in it). Deleting a project deletes its documents.
|
|
78
|
+
- Permissions `docs:read`, `docs:write`, `docs:publish` and `docs:delete`. Publishing (a
|
|
79
|
+
version for the exports, or whether they deliver it: `POST .../publish`, `PATCH` with
|
|
80
|
+
`exportable`) needs `docs:publish`, not `docs:write`. The OAuth scopes grant reading
|
|
81
|
+
(`streamby:read`) and writing (`streamby:write`); an app publishes and deletes none, whatever
|
|
82
|
+
its scopes: what it edits stays a draft.
|
|
83
|
+
- In MongoDB, a save writes the document and its version in a transaction when the server has
|
|
84
|
+
them (a replica set or a sharded cluster); on a standalone server, two writes.
|
|
85
|
+
- MCP tools `list_documents`, `get_document`, `list_document_versions`, `create_document`,
|
|
86
|
+
`update_document` and `update_document_section`.
|
|
87
|
+
- An export's `documentsNode` delivers the published version of the project's exportable
|
|
88
|
+
documents (all, one folder's, or some), never a draft.
|
|
89
|
+
- A `documentEvent` to the project's active members on each change.
|
|
90
|
+
- Types `ProjectDocument`, `DocumentVersion`, `DocumentAuthor` and `DocumentKind`.
|
|
91
|
+
|
|
3
92
|
## 0.41.0
|
|
4
93
|
|
|
5
94
|
### Added
|
package/README.md
CHANGED
|
@@ -132,8 +132,9 @@ hash is stored, with a short prefix to tell it apart.
|
|
|
132
132
|
| Production | `production:read`, `production:write`, `production:delete` | Sequences, shots and tasks. |
|
|
133
133
|
| Workflow | `workflow:read`, `workflow:write` | The workflow and its pipelines. |
|
|
134
134
|
| Exports | `exports:read`, `exports:write` | The project's exports. |
|
|
135
|
-
| Rights | `rights:read`, `rights:write` | Assets'
|
|
135
|
+
| Rights | `rights:read`, `rights:write` | Assets' license metadata, and reading credits sheets. |
|
|
136
136
|
| Jobs | `jobs:read`, `jobs:run` | Following jobs; starting them (transcoding, rendering, AI, publishing a deliverable). |
|
|
137
|
+
| Documents | `docs:read`, `docs:write`, `docs:publish`, `docs:delete` | The project's documents: reading them and their history; creating, editing and moving them; publishing them (the version its exports deliver, and whether they deliver it); deleting them. |
|
|
137
138
|
| Account | `projects:create` | Creating projects. A user's token only; the token reaches what it creates. |
|
|
138
139
|
|
|
139
140
|
`PROJECT_PERMISSIONS` and `ACCOUNT_PERMISSIONS` are exported. A route no permission covers
|
|
@@ -185,15 +186,16 @@ approved, and never more than the user can: their role in each project still app
|
|
|
185
186
|
|
|
186
187
|
| Scope | Permissions |
|
|
187
188
|
|---|---|
|
|
188
|
-
| `streamby:read` | Every `:read` permission: `project:read`, `members:read`, `assets:read`, `production:read`, `workflow:read`, `exports:read`, `rights:read`, `jobs:read`. |
|
|
189
|
-
| `streamby:write` | Those, and making and changing: `project:update`, `assets:write`, `production:write`, `workflow:write`, `exports:write`, `rights:write`, `jobs:run`, `projects:create`. |
|
|
189
|
+
| `streamby:read` | Every `:read` permission: `project:read`, `members:read`, `assets:read`, `production:read`, `workflow:read`, `exports:read`, `rights:read`, `jobs:read`, `docs:read`. |
|
|
190
|
+
| `streamby:write` | Those, and making and changing: `project:update`, `assets:write`, `production:write`, `workflow:write`, `exports:write`, `rights:write`, `jobs:run`, `projects:create`, `docs:write`. |
|
|
190
191
|
|
|
191
192
|
No scope grants deleting (`*:delete`, `project:delete`) nor managing members. And since some
|
|
192
193
|
write permissions also reach routes that delete (`project:update` a connection, a worker or an
|
|
193
194
|
integration; `workflow:write` a pipeline; `exports:write` an export), **an app's `DELETE` is
|
|
194
195
|
refused (403) whatever its route**, but taking a deliverable's mark off, which only forgets the
|
|
195
|
-
mark (the asset stays).
|
|
196
|
-
|
|
196
|
+
mark (the asset stays). Nor does an app publish a document (`POST .../publish`, or a `PATCH`
|
|
197
|
+
changing `exportable`), whatever its scopes: what it edits stays a draft until someone publishes
|
|
198
|
+
it. API tokens aren't affected. Other scopes (`profile`, `email`) grant nothing in StreamBy. `OAUTH_SCOPE_PERMISSIONS` is exported.
|
|
197
199
|
|
|
198
200
|
**Telling clients where to sign in**
|
|
199
201
|
|
|
@@ -283,7 +285,7 @@ A token sees only the tools whose permissions it has (an app: those its scopes g
|
|
|
283
285
|
| `list_assets` (by category, optionally in one storage connection, and filtered by name), `get_asset_metadata` | `assets:read` |
|
|
284
286
|
| `update_asset_metadata` | `assets:write` |
|
|
285
287
|
| `delete_asset` | `assets:delete` |
|
|
286
|
-
| `get_asset_rights` / `set_asset_rights` | `rights:read` / `rights:write` |
|
|
288
|
+
| `get_asset_rights`, `get_credits_sheet` / `set_asset_rights` | `rights:read` / `rights:write` |
|
|
287
289
|
| `get_workflow`, `list_pipelines`, `get_pipeline` | `workflow:read` |
|
|
288
290
|
| `create_pipeline`, `update_pipeline` | `workflow:write` |
|
|
289
291
|
| `list_exports` | `project:read` and `exports:read` |
|
|
@@ -296,7 +298,9 @@ A token sees only the tools whose permissions it has (an app: those its scopes g
|
|
|
296
298
|
| `create_sequence`, `create_shot`, `create_task` | `production:write` and `workflow:write` |
|
|
297
299
|
| `delete_sequence`, `delete_shot`, `delete_task` | `production:delete` |
|
|
298
300
|
| `list_asset_versions`, `get_asset_download_url`, `list_deliverables` | `assets:read` |
|
|
299
|
-
| `request_asset_upload
|
|
301
|
+
| `request_asset_upload` (with license metadata, also `rights:write`), `mark_deliverable`, `remove_deliverable` | `assets:write` |
|
|
302
|
+
| `list_documents`, `get_document`, `list_document_versions` | `docs:read` |
|
|
303
|
+
| `create_document`, `update_document`, `update_document_section` | `docs:write` |
|
|
300
304
|
|
|
301
305
|
The tools take the path a user does in the UI, and each one's description says where its
|
|
302
306
|
result shows there. Production data lives under a production node of the project's workflow:
|
|
@@ -446,6 +450,87 @@ person removes it in their account's connected apps (in Nhexa, accounts.nhexa.cl
|
|
|
446
450
|
|
|
447
451
|
---
|
|
448
452
|
|
|
453
|
+
## 📄 Documents
|
|
454
|
+
|
|
455
|
+
Each project keeps its documents: markdown its team reads and edits (its concept, its
|
|
456
|
+
architecture, its decisions), and agents read and keep up to date through the MCP server. They're
|
|
457
|
+
kept in the main database (the `documents` and `document_versions` tables, created at startup,
|
|
458
|
+
or collections), each one in a single project, in folders.
|
|
459
|
+
|
|
460
|
+
- **Every save is a version**, with who saved it and when: a user, an API token, or an app on a
|
|
461
|
+
user's behalf (`{ userId, username, via: 'session' | 'apiToken' | 'oauth', tokenId?, clientId?, clientName? }`).
|
|
462
|
+
- **Nothing is overwritten without notice.** A change says the version it was made on
|
|
463
|
+
(`baseVersion`); if someone saved another one since, it's refused with a 409 and the document
|
|
464
|
+
as it is now, to read again.
|
|
465
|
+
- **A section** is what's under a heading (`## Architecture`), up to the next one of the same
|
|
466
|
+
level or above: it can be replaced alone, the rest untouched.
|
|
467
|
+
- **Drafts and published versions.** A document marked exportable delivers, through the project's
|
|
468
|
+
exports, only the version published from its history; editing it changes nothing exported until
|
|
469
|
+
another version is published. An export's `documentsNode` (on the StreamBy node's data input)
|
|
470
|
+
delivers them, `[{ id, title, parentId, position, version, content, publishedAt }]`: all the
|
|
471
|
+
project's, those in one folder (`data.folderId`), or some (`data.documentIds`).
|
|
472
|
+
- **In real time**, a `documentEvent` reaches the project's active members on each change:
|
|
473
|
+
`{ projectId, action: 'created' | 'saved' | 'changed' | 'deleted', documentIds, version?, title?, author }`
|
|
474
|
+
(`saved` is a new version; `changed`, a move or a publication).
|
|
475
|
+
|
|
476
|
+
| Route | Permission |
|
|
477
|
+
|---|---|
|
|
478
|
+
| `GET /projects/:projectId/documents`: the tree, without contents | `docs:read` |
|
|
479
|
+
| `POST /projects/:projectId/documents` `{ title, content?, kind?: 'document' \| 'folder' \| 'credits', parentId?, position? }` | `docs:write` |
|
|
480
|
+
| `GET /projects/:projectId/documents/:docId` | `docs:read` |
|
|
481
|
+
| `PATCH /projects/:projectId/documents/:docId` `{ baseVersion, title?, content? }` (a new version) and/or `{ parentId?, position? }` | `docs:write` |
|
|
482
|
+
| `PATCH /projects/:projectId/documents/:docId` `{ exportable }` | `docs:write` and `docs:publish` |
|
|
483
|
+
| `PATCH /projects/:projectId/documents/:docId/sections` `{ baseVersion, heading, content }` | `docs:write` |
|
|
484
|
+
| `GET /projects/:projectId/documents/:docId/versions` and `/versions/:version` | `docs:read` |
|
|
485
|
+
| `POST /projects/:projectId/documents/:docId/publish` `{ version }` (`null`: none) | `docs:publish` |
|
|
486
|
+
| `DELETE /projects/:projectId/documents/:docId` (a folder, with everything in it) | `docs:delete` |
|
|
487
|
+
|
|
488
|
+
Editors and admins create, edit, publish and delete them; viewers read them. An app connected
|
|
489
|
+
with OAuth reads and edits them, but publishes and deletes none: what it edits stays a draft.
|
|
490
|
+
|
|
491
|
+
In MongoDB, a save writes the document and its version in a transaction when the server has them
|
|
492
|
+
(a replica set or a sharded cluster). A standalone server doesn't: there they're two writes.
|
|
493
|
+
|
|
494
|
+
---
|
|
495
|
+
|
|
496
|
+
## ©️ License metadata and credits sheets
|
|
497
|
+
|
|
498
|
+
Each asset can carry its **license metadata**, for all its versions: where it was taken from
|
|
499
|
+
(`sourceUrl`), who made it (`creatorName`, `creatorUrl`), its license (`licenseType`) and
|
|
500
|
+
`comments`. All are optional; the links are http(s) URLs, and the license is one of
|
|
501
|
+
`CC BY`, `CC BY-SA`, `CC BY-ND`, `CC BY-NC`, `CC BY-NC-SA`, `CC BY-NC-ND`, `CC0`, `Free Standard`,
|
|
502
|
+
`Standard` and `Editorial` (`LICENSE_TYPES`, exported). It's kept in the `asset_rights`
|
|
503
|
+
collection, the only place it lives, and goes with its asset or its project when they're deleted.
|
|
504
|
+
It can be given with the upload: `licenseType`, `sourceUrl`, `creatorName`, `creatorUrl` and
|
|
505
|
+
`comments` as query params of `GET .../upload-url` (an API token needs `rights:write` too).
|
|
506
|
+
|
|
507
|
+
A **credits sheet** is a project document of kind `credits`, made by a Credits sheet node of the
|
|
508
|
+
workflow (its `data.documentId`). It keeps only what feeds it, as its content:
|
|
509
|
+
`{ "assetIds": [...], "folderIds": [...] }` (assets and Storage folders picked, with everything
|
|
510
|
+
inside them); the Ingest nodes connected to its node feed it too, with the asset each imports
|
|
511
|
+
(`data.fileId`). Its rows are made each time it's read, from each asset's metadata, so two sheets
|
|
512
|
+
with the same asset show the same, and editing a row is setting its asset's metadata. Deleting
|
|
513
|
+
the node leaves the document; an asset deleted leaves the sheet.
|
|
514
|
+
|
|
515
|
+
A sheet is **complete** when it has an asset and each one has its creator, source and license;
|
|
516
|
+
**incomplete** otherwise, each row listing what it lacks (`missing`).
|
|
517
|
+
|
|
518
|
+
- **In real time**, an `assetRightsEvent` reaches the project's active members on each change:
|
|
519
|
+
`{ projectId, action: 'saved' | 'deleted', assetIds, rights? }` (`deleted`: the asset is gone).
|
|
520
|
+
|
|
521
|
+
| Route | Permission |
|
|
522
|
+
|---|---|
|
|
523
|
+
| `GET /projects/:projectId/assets/:assetId/rights` | `rights:read` |
|
|
524
|
+
| `PUT /projects/:projectId/assets/:assetId/rights` `{ sourceUrl?, creatorName?, creatorUrl?, licenseType?, comments? }` (the fields given; empty or `null` clears one) | `rights:write` |
|
|
525
|
+
| `GET /projects/:projectId/credits-sheets`: each with `{ id, title, status, rows, incomplete }` | `rights:read` |
|
|
526
|
+
| `GET /projects/:projectId/credits-sheets/:docId`: `{ document, sources, rows, status }` | `rights:read` |
|
|
527
|
+
|
|
528
|
+
Each row: `{ assetId, name, category, sourceUrl, creatorName, creatorUrl, licenseType, comments, missing, from }`,
|
|
529
|
+
`from` saying how it got there (`asset`, `folder`, `ingest`). Editors and admins set the metadata
|
|
530
|
+
(as they edit the asset); every member reads it.
|
|
531
|
+
|
|
532
|
+
---
|
|
533
|
+
|
|
449
534
|
## ⚙️ Processing workers
|
|
450
535
|
|
|
451
536
|
StreamBy doesn't process assets itself (optimizing a glTF, making LODs, transcoding...): it
|
|
@@ -551,6 +636,56 @@ A job its worker doesn't complete before its deadline is sent again, up to
|
|
|
551
636
|
|
|
552
637
|
---
|
|
553
638
|
|
|
639
|
+
## 🧭 The workflow defines the project
|
|
640
|
+
|
|
641
|
+
Each of a project's resources (a database, an API, a storage, a service, an export, a pipeline)
|
|
642
|
+
is a node of its workflow, bound to it by the resource's id in its data (`connectionId`,
|
|
643
|
+
`storageConnectionId`, `integrationConnectionId`, `exportId`, `pipelineId`). One node per
|
|
644
|
+
resource; a data source reading one of a database's tables is a node of its own.
|
|
645
|
+
|
|
646
|
+
- **From a section** (or an agent): adding a resource adds its node to the saved workflow, and
|
|
647
|
+
removing it removes its nodes.
|
|
648
|
+
- **Saving the workflow** (`PATCH /projects/:id/workflow` `{ nodeSchema, baseUpdatedAt }`) makes
|
|
649
|
+
the project what it defines, then answers `{ data, project, errors }`: the workflow, the
|
|
650
|
+
project's lists as they are then, and what couldn't be done. A node set to an integration the
|
|
651
|
+
project doesn't have (`"__connect__:<integrationId>"`) connects it; an API, a pipeline or an
|
|
652
|
+
export drafted on a node (`data.connectionDraft`, `data.pipelineDraft`, `data.exportDraft`) is
|
|
653
|
+
made; a resource whose nodes are all gone is removed.
|
|
654
|
+
- `baseUpdatedAt` (the workflow's `updatedAt` as it was read) is required with a `nodeSchema`,
|
|
655
|
+
and `nodeSchema` must be `{ nodes: [...], edges: [...] }`: a 400 otherwise. Saved over a newer
|
|
656
|
+
version, it's a 409 with the workflow as it is now.
|
|
657
|
+
- An app connected with OAuth removes nothing through it, as it deletes nothing: a save that
|
|
658
|
+
would remove resources is a 403 listing them (`removing`).
|
|
659
|
+
- A node holding a configuration of its own beside its resource's id (a data source reading one
|
|
660
|
+
table, an Ingest importing one file) consumes the resource: never merged into its node.
|
|
661
|
+
- **Reading it** (`GET`) never rewrites it, except once, for a workflow made before this: the
|
|
662
|
+
nodes once drawn from the project's lists are bound by their ids, a resource without a node gets
|
|
663
|
+
one, and two nodes of the same resource are merged.
|
|
664
|
+
|
|
665
|
+
---
|
|
666
|
+
|
|
667
|
+
## 🔌 Removing a project's resources
|
|
668
|
+
|
|
669
|
+
Before a connection (database, API, storage, service), an export or a pipeline is removed, the
|
|
670
|
+
UI asks what depends on it: `GET /projects/:id/dependents/:kind/:resourceId` (`kind`: `db`,
|
|
671
|
+
`api`, `storage`, `service`, `export`, `pipeline`; `project:read`) answers
|
|
672
|
+
`{ dependents: [{ type, name, id?, count? }] }`: the nodes that use it in the workflow, the
|
|
673
|
+
exports and the pipelines (`workflow`, `export`, `pipeline`: they stop working without it), and
|
|
674
|
+
what's kept for it (`files` and `folders` of a storage, `deliverables` and `shots` of an
|
|
675
|
+
export). Removing it changes none of them.
|
|
676
|
+
|
|
677
|
+
**Built-ins** (the host's database and storage) are disconnected like any connection, and what
|
|
678
|
+
the project keeps in them stays. They're connected again as any integration is, by its id in
|
|
679
|
+
the pool (`GET /user/integrations`): `POST /projects/:id/integrations` `{ integrationId }`, or
|
|
680
|
+
`POST .../connections/db` and `.../connections/storage` with `integrationId` (its name and type
|
|
681
|
+
come from the integration). One connected again keeps the id it had (`disconnectedBuiltins`
|
|
682
|
+
on the project), so its files and records are its own again.
|
|
683
|
+
|
|
684
|
+
Deleting an account's integration disconnects the project connections that use it;
|
|
685
|
+
`GET /user/integrations/:id/dependents` lists them first.
|
|
686
|
+
|
|
687
|
+
---
|
|
688
|
+
|
|
554
689
|
## 👥 Project roles
|
|
555
690
|
|
|
556
691
|
What a user can do in a project depends on their role in that project, as an active member
|
|
@@ -560,7 +695,7 @@ What a user can do in a project depends on their role in that project, as an act
|
|
|
560
695
|
| Role | Can |
|
|
561
696
|
|---|---|
|
|
562
697
|
| `viewer` | See the project and its content, and archive the project in their own list. |
|
|
563
|
-
| `editor` | Everything a viewer can, plus change the project: its details, connections, exports, files and folders, workflow, pipelines, production, reviews, rights, and start jobs. |
|
|
698
|
+
| `editor` | Everything a viewer can, plus change the project: its details, connections, exports, files and folders, workflow, pipelines, production, reviews, rights, documents, and start jobs. |
|
|
564
699
|
| `admin` | Everything an editor can, plus manage the project's members and delete the project. |
|
|
565
700
|
|
|
566
701
|
A project's public exports (`GET /streamby/:projectId/export/:exportName`) are read without a
|
|
@@ -101,6 +101,7 @@ const createTables = async (pool) => {
|
|
|
101
101
|
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS workflow JSONB;
|
|
102
102
|
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS pipelines JSONB DEFAULT '[]';
|
|
103
103
|
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS "builtinBackfilledAt" TIMESTAMP WITH TIME ZONE;
|
|
104
|
+
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS "disconnectedBuiltins" JSONB DEFAULT '[]';
|
|
104
105
|
-- Written when a project is made (routes/project) and by its connection routes.
|
|
105
106
|
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS public BOOLEAN DEFAULT TRUE;
|
|
106
107
|
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS category TEXT;
|
|
@@ -254,6 +255,38 @@ const createTables = async (pool) => {
|
|
|
254
255
|
ALTER TABLE streamby.mcp_audit ALTER COLUMN "tokenId" DROP NOT NULL;
|
|
255
256
|
`);
|
|
256
257
|
console.log('✅ "mcp_audit" table ensured to exist.');
|
|
258
|
+
// Projects' documents and their versions (see services/documents). Main db only.
|
|
259
|
+
await pool.query(`
|
|
260
|
+
CREATE TABLE IF NOT EXISTS streamby.documents (
|
|
261
|
+
id TEXT PRIMARY KEY,
|
|
262
|
+
"projectId" VARCHAR(255) NOT NULL,
|
|
263
|
+
kind VARCHAR(20) NOT NULL DEFAULT 'document',
|
|
264
|
+
title TEXT NOT NULL,
|
|
265
|
+
"parentId" TEXT,
|
|
266
|
+
position INTEGER NOT NULL DEFAULT 0,
|
|
267
|
+
content TEXT NOT NULL DEFAULT '',
|
|
268
|
+
version INTEGER NOT NULL,
|
|
269
|
+
exportable BOOLEAN NOT NULL DEFAULT FALSE,
|
|
270
|
+
"publishedVersion" INTEGER,
|
|
271
|
+
"createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
|
272
|
+
"createdBy" JSONB NOT NULL,
|
|
273
|
+
"updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
|
274
|
+
"updatedBy" JSONB NOT NULL
|
|
275
|
+
);
|
|
276
|
+
CREATE INDEX IF NOT EXISTS documents_project_idx ON streamby.documents ("projectId");
|
|
277
|
+
CREATE TABLE IF NOT EXISTS streamby.document_versions (
|
|
278
|
+
"documentId" TEXT NOT NULL,
|
|
279
|
+
"projectId" VARCHAR(255) NOT NULL,
|
|
280
|
+
version INTEGER NOT NULL,
|
|
281
|
+
title TEXT NOT NULL,
|
|
282
|
+
content TEXT NOT NULL,
|
|
283
|
+
author JSONB NOT NULL,
|
|
284
|
+
"createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
|
285
|
+
PRIMARY KEY ("documentId", version)
|
|
286
|
+
);
|
|
287
|
+
CREATE INDEX IF NOT EXISTS document_versions_project_idx ON streamby.document_versions ("projectId");
|
|
288
|
+
`);
|
|
289
|
+
console.log('✅ "documents" and "document_versions" tables ensured to exist.');
|
|
257
290
|
}
|
|
258
291
|
catch (error) {
|
|
259
292
|
console.error('❌ Error ensuring tables exist:', error);
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
export { createStreamByRouter } from './middleware/createRouter';
|
|
2
2
|
export { emitToUser } from './services/wsHub';
|
|
3
3
|
export { createNotification } from './services/notification';
|
|
4
|
-
export type { UserPlan, UserSubscription, Auth, ApiTokenScope, ApiTokenKind, ApiTokenPermission, ProjectPermission, AccountPermission, OAuthClientInfo, OAuthResourceConfig } from './types';
|
|
4
|
+
export type { UserPlan, UserSubscription, Auth, ApiTokenScope, ApiTokenKind, ApiTokenPermission, ProjectPermission, AccountPermission, OAuthClientInfo, OAuthResourceConfig, ProjectDocument, DocumentVersion, DocumentAuthor, DocumentKind } from './types';
|
|
5
5
|
export { resolveApiToken, API_TOKEN_PREFIX } from './services/apiTokens';
|
|
6
6
|
export { PROJECT_PERMISSIONS, ACCOUNT_PERMISSIONS } from './services/apiTokenPermissions';
|
|
7
|
+
export { LICENSE_TYPES } from './services/rights';
|
|
8
|
+
export type { AssetRights, LicenseType } from './services/rights';
|
|
9
|
+
export { CREDITS_SHEET_NODE } from './services/creditsSheets';
|
|
10
|
+
export type { CreditsSheet, SheetRow, SheetSources, SheetStatus } from './services/creditsSheets';
|
|
7
11
|
export type { ResolvedApiToken } from './services/apiTokens';
|
|
8
12
|
export { createOAuthTokenVerifier, looksLikeOAuthAccessToken, protectedResourceMetadata, defaultResourceMetadataUrl, permissionsForOAuthScopes, OAUTH_SCOPES, OAUTH_SCOPE_PERMISSIONS, } from './services/oauthTokens';
|
|
9
13
|
export type { VerifiedOAuthToken, OAuthTokenVerifierOptions, Jwks, Jwk, OAuthScope } from './services/oauthTokens';
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.OAUTH_SCOPE_PERMISSIONS = exports.OAUTH_SCOPES = exports.permissionsForOAuthScopes = exports.defaultResourceMetadataUrl = exports.protectedResourceMetadata = exports.looksLikeOAuthAccessToken = exports.createOAuthTokenVerifier = exports.ACCOUNT_PERMISSIONS = exports.PROJECT_PERMISSIONS = exports.API_TOKEN_PREFIX = exports.resolveApiToken = exports.createNotification = exports.emitToUser = exports.createStreamByRouter = void 0;
|
|
3
|
+
exports.OAUTH_SCOPE_PERMISSIONS = exports.OAUTH_SCOPES = exports.permissionsForOAuthScopes = exports.defaultResourceMetadataUrl = exports.protectedResourceMetadata = exports.looksLikeOAuthAccessToken = exports.createOAuthTokenVerifier = exports.CREDITS_SHEET_NODE = exports.LICENSE_TYPES = exports.ACCOUNT_PERMISSIONS = exports.PROJECT_PERMISSIONS = exports.API_TOKEN_PREFIX = exports.resolveApiToken = exports.createNotification = exports.emitToUser = exports.createStreamByRouter = void 0;
|
|
4
4
|
var createRouter_1 = require("./middleware/createRouter");
|
|
5
5
|
Object.defineProperty(exports, "createStreamByRouter", { enumerable: true, get: function () { return createRouter_1.createStreamByRouter; } });
|
|
6
6
|
var wsHub_1 = require("./services/wsHub");
|
|
@@ -13,6 +13,10 @@ Object.defineProperty(exports, "API_TOKEN_PREFIX", { enumerable: true, get: func
|
|
|
13
13
|
var apiTokenPermissions_1 = require("./services/apiTokenPermissions");
|
|
14
14
|
Object.defineProperty(exports, "PROJECT_PERMISSIONS", { enumerable: true, get: function () { return apiTokenPermissions_1.PROJECT_PERMISSIONS; } });
|
|
15
15
|
Object.defineProperty(exports, "ACCOUNT_PERMISSIONS", { enumerable: true, get: function () { return apiTokenPermissions_1.ACCOUNT_PERMISSIONS; } });
|
|
16
|
+
var rights_1 = require("./services/rights");
|
|
17
|
+
Object.defineProperty(exports, "LICENSE_TYPES", { enumerable: true, get: function () { return rights_1.LICENSE_TYPES; } });
|
|
18
|
+
var creditsSheets_1 = require("./services/creditsSheets");
|
|
19
|
+
Object.defineProperty(exports, "CREDITS_SHEET_NODE", { enumerable: true, get: function () { return creditsSheets_1.CREDITS_SHEET_NODE; } });
|
|
16
20
|
var oauthTokens_1 = require("./services/oauthTokens");
|
|
17
21
|
Object.defineProperty(exports, "createOAuthTokenVerifier", { enumerable: true, get: function () { return oauthTokens_1.createOAuthTokenVerifier; } });
|
|
18
22
|
Object.defineProperty(exports, "looksLikeOAuthAccessToken", { enumerable: true, get: function () { return oauthTokens_1.looksLikeOAuthAccessToken; } });
|
package/dist/mcp/tools.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.TOOLS = void 0;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
|
+
const rights_1 = require("../services/rights");
|
|
5
6
|
const tool = (definition) => definition;
|
|
6
7
|
const id = (what) => zod_1.z.string().min(1).describe(`The ${what}'s id`);
|
|
7
8
|
const projectId = id('project');
|
|
@@ -25,14 +26,13 @@ const taskFields = {
|
|
|
25
26
|
dueDate: zod_1.z.string().optional().describe('An ISO 8601 date'),
|
|
26
27
|
notes: zod_1.z.string().optional(),
|
|
27
28
|
};
|
|
29
|
+
// An asset's license metadata: what crediting it takes. Each is optional; an empty one clears it.
|
|
28
30
|
const rightsFields = {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
usageRestrictions: zod_1.z.string().optional(),
|
|
35
|
-
notes: zod_1.z.string().optional(),
|
|
31
|
+
sourceUrl: zod_1.z.string().optional().describe('Where the asset was taken from (an http(s) URL)'),
|
|
32
|
+
creatorName: zod_1.z.string().optional().describe('Who made it'),
|
|
33
|
+
creatorUrl: zod_1.z.string().optional().describe("The creator's page (an http(s) URL)"),
|
|
34
|
+
licenseType: zod_1.z.enum(rights_1.LICENSE_TYPES).or(zod_1.z.literal('')).optional().describe('Its license'),
|
|
35
|
+
comments: zod_1.z.string().optional(),
|
|
36
36
|
};
|
|
37
37
|
// Only the fields given (an unset optional field isn't sent as undefined).
|
|
38
38
|
const defined = (fields) => Object.fromEntries(Object.entries(fields).filter(([, value]) => value !== undefined));
|
|
@@ -86,7 +86,7 @@ exports.TOOLS = [
|
|
|
86
86
|
}),
|
|
87
87
|
tool({
|
|
88
88
|
name: 'get_asset_rights', title: "Get an asset's rights", permissions: ['rights:read'],
|
|
89
|
-
description: "An asset's
|
|
89
|
+
description: "An asset's license metadata: where it was taken from, its creator (name and link), its license and comments; null if it has none.",
|
|
90
90
|
input: { projectId, assetId: id('asset') },
|
|
91
91
|
projectOf: byProject,
|
|
92
92
|
call: ({ projectId, assetId }) => ({ method: 'GET', path: `/projects/${enc(projectId)}/assets/${enc(assetId)}/rights` }),
|
|
@@ -164,7 +164,7 @@ exports.TOOLS = [
|
|
|
164
164
|
}),
|
|
165
165
|
tool({
|
|
166
166
|
name: 'set_asset_rights', title: "Set an asset's rights", permissions: ['rights:write'], write: true,
|
|
167
|
-
description:
|
|
167
|
+
description: `Sets an asset's license metadata (the fields given; an empty one clears it): where it was taken from, its creator (name and link), its license (one of: ${rights_1.LICENSE_TYPES.join(', ')}) and comments. It shows in the file's Rights tab, in Storage, and in every credits sheet the asset is in.`,
|
|
168
168
|
input: { projectId, assetId: id('asset'), ...rightsFields },
|
|
169
169
|
projectOf: byProject,
|
|
170
170
|
call: ({ projectId, assetId, ...rights }) => ({ method: 'PUT', path: `/projects/${enc(projectId)}/assets/${enc(assetId)}/rights`, body: defined(rights) }),
|
|
@@ -245,6 +245,7 @@ exports.TOOLS = [
|
|
|
245
245
|
"It doesn't upload anything: the file is sent by the tool that has it (for example Blender's MCP, running Python), with an HTTP PUT of its bytes to `upload.url` and the `upload.headers` given, before `expiresAt`.",
|
|
246
246
|
"The URL works for that file only. A new version keeps the asset's URL (it replaces what it serves); the earlier ones are archived and can still be downloaded.",
|
|
247
247
|
"Categories: images, audios, videos, 3d-models (.glb, .gltf, .obj, .fbx, .stl, .ply); contentType must match it.",
|
|
248
|
+
"For a new asset taken from someone else (a CC model, a stock image), give its license metadata too, so it's credited (it needs the rights:write permission); for a new version, set_asset_rights changes it.",
|
|
248
249
|
].join(' '),
|
|
249
250
|
input: {
|
|
250
251
|
projectId,
|
|
@@ -253,10 +254,14 @@ exports.TOOLS = [
|
|
|
253
254
|
contentType: zod_1.z.string().min(1).describe('Its MIME type (e.g. model/gltf-binary, image/png, video/mp4)'),
|
|
254
255
|
assetId: zod_1.z.string().optional().describe('An existing asset to upload a new version of; a new asset if not given'),
|
|
255
256
|
connectionId: zod_1.z.string().optional().describe("A storage connection to store it in; the project's built-in storage if not given"),
|
|
257
|
+
...rightsFields,
|
|
256
258
|
},
|
|
257
259
|
projectOf: byProject,
|
|
258
|
-
call: ({ projectId, category, fileName, contentType, assetId, connectionId }) => {
|
|
259
|
-
const query = new URLSearchParams({
|
|
260
|
+
call: ({ projectId, category, fileName, contentType, assetId, connectionId, ...rights }) => {
|
|
261
|
+
const query = new URLSearchParams({
|
|
262
|
+
fileName, contentType, ...(connectionId && !assetId ? { category } : {}),
|
|
263
|
+
...(assetId ? {} : defined(rights)),
|
|
264
|
+
});
|
|
260
265
|
const files = connectionId ? `/projects/${enc(projectId)}/connections/storage/${enc(connectionId)}` : `/projects/${enc(projectId)}/storage`;
|
|
261
266
|
const path = assetId
|
|
262
267
|
? `${files}/files/${enc(assetId)}/replace-url`
|
|
@@ -358,6 +363,83 @@ exports.TOOLS = [
|
|
|
358
363
|
call: ({ projectId, jobId }) => ({ method: 'POST', path: `/projects/${enc(projectId)}/jobs/${enc(jobId)}/retry` }),
|
|
359
364
|
pick: (data) => data?.job,
|
|
360
365
|
}),
|
|
366
|
+
// ── Credits sheets ──
|
|
367
|
+
tool({
|
|
368
|
+
name: 'get_credits_sheet', title: 'Read a credits sheet', permissions: ['rights:read'],
|
|
369
|
+
description: "A credits sheet (a document of kind 'credits', made by a Credits sheet node of the workflow; list_documents lists them): a row per asset fed to it (picked on it, in a Storage folder picked, or imported by an Ingest node connected to it) with that asset's license metadata as it is now, and its status: 'complete' when it has an asset and each has its creator, source and license; 'incomplete' otherwise, each row listing what it lacks (`missing`). To fill a row in, set_asset_rights on its asset.",
|
|
370
|
+
input: { projectId, documentId: id('credits sheet') },
|
|
371
|
+
projectOf: byProject,
|
|
372
|
+
call: ({ projectId, documentId }) => ({ method: 'GET', path: `/projects/${enc(projectId)}/credits-sheets/${enc(documentId)}` }),
|
|
373
|
+
pick: (data) => data && { id: data.document?.id, title: data.document?.title, status: data.status, rows: data.rows },
|
|
374
|
+
}),
|
|
375
|
+
// ── Documents ──
|
|
376
|
+
tool({
|
|
377
|
+
name: 'list_documents', title: 'List documents', permissions: ['docs:read'],
|
|
378
|
+
description: "A project's documents (its concept, architecture, decisions...), as the tree of its Documents section shows them: each one's id, title, folder (parentId; a folder's kind is 'folder'), position, current version and the version published through its exports, without their content (read one with get_document).",
|
|
379
|
+
input: { projectId },
|
|
380
|
+
projectOf: byProject,
|
|
381
|
+
call: ({ projectId }) => ({ method: 'GET', path: `/projects/${enc(projectId)}/documents` }),
|
|
382
|
+
pick: (data) => data?.documents ?? [],
|
|
383
|
+
}),
|
|
384
|
+
tool({
|
|
385
|
+
name: 'get_document', title: 'Read a document', permissions: ['docs:read'],
|
|
386
|
+
description: "A document's markdown and its current version, the one an update is made on. With `version`, that past version instead (see list_document_versions).",
|
|
387
|
+
input: { projectId, documentId: id('document'), version: zod_1.z.number().int().min(1).optional().describe('A past version to read; the current one if not given') },
|
|
388
|
+
projectOf: byProject,
|
|
389
|
+
call: ({ projectId, documentId, version }) => ({
|
|
390
|
+
method: 'GET',
|
|
391
|
+
path: `/projects/${enc(projectId)}/documents/${enc(documentId)}${version ? `/versions/${version}` : ''}`,
|
|
392
|
+
}),
|
|
393
|
+
pick: (data) => data?.document ?? data?.version,
|
|
394
|
+
}),
|
|
395
|
+
tool({
|
|
396
|
+
name: 'list_document_versions', title: "List a document's versions", permissions: ['docs:read'],
|
|
397
|
+
description: "A document's history, the latest first: each version's number, title, who saved it (a user, an API token, or an app on a user's behalf) and when.",
|
|
398
|
+
input: { projectId, documentId: id('document') },
|
|
399
|
+
projectOf: byProject,
|
|
400
|
+
call: ({ projectId, documentId }) => ({ method: 'GET', path: `/projects/${enc(projectId)}/documents/${enc(documentId)}/versions` }),
|
|
401
|
+
pick: (data) => data?.versions ?? [],
|
|
402
|
+
}),
|
|
403
|
+
tool({
|
|
404
|
+
name: 'create_document', title: 'Create a document', permissions: ['docs:write'], write: true,
|
|
405
|
+
description: "Creates a markdown document (or, with kind 'folder', a folder to group them) in a project's Documents section, at the end of its folder. It's a draft: nothing is exported until someone publishes a version of it in the UI.",
|
|
406
|
+
input: {
|
|
407
|
+
projectId,
|
|
408
|
+
title: zod_1.z.string().min(1),
|
|
409
|
+
content: zod_1.z.string().optional().describe('Its markdown'),
|
|
410
|
+
kind: zod_1.z.enum(['document', 'folder']).optional(),
|
|
411
|
+
parentId: zod_1.z.string().optional().describe("The folder it goes in (a folder's id); at the top if not given"),
|
|
412
|
+
},
|
|
413
|
+
projectOf: byProject,
|
|
414
|
+
call: ({ projectId, ...fields }) => ({ method: 'POST', path: `/projects/${enc(projectId)}/documents`, body: defined(fields) }),
|
|
415
|
+
pick: (data) => data?.document,
|
|
416
|
+
}),
|
|
417
|
+
tool({
|
|
418
|
+
name: 'update_document', title: 'Update a document', permissions: ['docs:write'], write: true,
|
|
419
|
+
description: "Saves a new version of a document: its whole markdown, its title, or both. `baseVersion` is the version it was read at (get_document): if someone saved another one since, nothing is saved and the answer says so; read it again and make the change on the new version. To change one part, update_document_section rewrites less.",
|
|
420
|
+
input: {
|
|
421
|
+
projectId, documentId: id('document'),
|
|
422
|
+
baseVersion: zod_1.z.number().int().min(1).describe('The version the change was made on'),
|
|
423
|
+
title: zod_1.z.string().min(1).optional(),
|
|
424
|
+
content: zod_1.z.string().optional().describe('The whole new markdown'),
|
|
425
|
+
},
|
|
426
|
+
projectOf: byProject,
|
|
427
|
+
call: ({ projectId, documentId, ...fields }) => ({ method: 'PATCH', path: `/projects/${enc(projectId)}/documents/${enc(documentId)}`, body: defined(fields) }),
|
|
428
|
+
pick: (data) => data?.document,
|
|
429
|
+
}),
|
|
430
|
+
tool({
|
|
431
|
+
name: 'update_document_section', title: "Update a document's section", permissions: ['docs:write'], write: true,
|
|
432
|
+
description: "Saves a new version of a document with only what's under one of its headings replaced (up to the next heading of the same level or above), the rest untouched. `heading` is the heading as written (\"## Architecture\") or its title alone. `baseVersion` is the version it was read at: if the document changed since, nothing is saved; read it again.",
|
|
433
|
+
input: {
|
|
434
|
+
projectId, documentId: id('document'),
|
|
435
|
+
baseVersion: zod_1.z.number().int().min(1).describe('The version the change was made on'),
|
|
436
|
+
heading: zod_1.z.string().min(1).describe('The section\'s heading, e.g. "## Architecture"'),
|
|
437
|
+
content: zod_1.z.string().describe("The section's new markdown, without its heading"),
|
|
438
|
+
},
|
|
439
|
+
projectOf: byProject,
|
|
440
|
+
call: ({ projectId, documentId, ...fields }) => ({ method: 'PATCH', path: `/projects/${enc(projectId)}/documents/${enc(documentId)}/sections`, body: fields }),
|
|
441
|
+
pick: (data) => data?.document,
|
|
442
|
+
}),
|
|
361
443
|
// ── Projects ──
|
|
362
444
|
tool({
|
|
363
445
|
name: 'create_project', title: 'Create a project', permissions: ['projects:create'], write: true,
|
|
@@ -9,6 +9,7 @@ const crypto_1 = __importDefault(require("crypto"));
|
|
|
9
9
|
const manager_1 = require("../models/manager");
|
|
10
10
|
const auth_1 = require("../utils/auth");
|
|
11
11
|
const auth_2 = require("../services/auth");
|
|
12
|
+
const workflowResources_1 = require("../services/workflowResources");
|
|
12
13
|
// The node types that make a workflow's production section (as the UI groups them).
|
|
13
14
|
const PRODUCTION_NODE_TYPES = new Set(['shotNode', 'assemblyNode', 'masterNode']);
|
|
14
15
|
/** A Shot node, as the UI makes one dropped on the canvas without a position. */
|
|
@@ -32,14 +33,19 @@ async function ensureWorkflowNode(requirement, projectId, auth, api) {
|
|
|
32
33
|
const project = await (0, manager_1.getModel)('projects').findOne({ _id: projectId });
|
|
33
34
|
if (!project || !(0, auth_1.activeMemberOf)(project, auth.userId))
|
|
34
35
|
return null;
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
// The workflow as it's answered (brought to define the project, if it wasn't yet), and saved
|
|
37
|
+
// on that version: what changed since isn't undone.
|
|
38
|
+
const workflow = await (0, workflowResources_1.readWorkflow)(project);
|
|
39
|
+
const nodes = Array.isArray(workflow?.nodeSchema?.nodes) ? workflow.nodeSchema.nodes : [];
|
|
37
40
|
if (nodes.some(node => PRODUCTION_NODE_TYPES.has(node?.type)))
|
|
38
41
|
return null;
|
|
39
42
|
const answer = await api({
|
|
40
43
|
method: 'PATCH',
|
|
41
44
|
path: `/projects/${encodeURIComponent(projectId)}/workflow`,
|
|
42
|
-
body: {
|
|
45
|
+
body: {
|
|
46
|
+
nodeSchema: { nodes: [...nodes, (0, exports.newShotNode)()], edges: Array.isArray(workflow?.nodeSchema?.edges) ? workflow.nodeSchema.edges : [] },
|
|
47
|
+
baseUpdatedAt: new Date(workflow.updatedAt).toISOString(),
|
|
48
|
+
},
|
|
43
49
|
});
|
|
44
50
|
if (answer.status >= 200 && answer.status < 300)
|
|
45
51
|
return null;
|
|
@@ -44,11 +44,15 @@ const worker_1 = require("./routes/worker");
|
|
|
44
44
|
const workerCallback_1 = require("./routes/workerCallback");
|
|
45
45
|
const workerJobs_1 = require("../services/workerJobs");
|
|
46
46
|
const deliverable_1 = require("./routes/deliverable");
|
|
47
|
+
const document_1 = require("./routes/document");
|
|
48
|
+
const projectResources_1 = require("./routes/projectResources");
|
|
49
|
+
const documents_1 = require("../services/documents");
|
|
47
50
|
function createStreamByRouter(config) {
|
|
48
51
|
const router = express_1.default.Router();
|
|
49
52
|
if (config.encrypt)
|
|
50
53
|
(0, encryption_1.setEncryptionKey)(config.encrypt, config.encryptPreviousKeys);
|
|
51
54
|
(0, jobStore_1.setJobStoreDatabase)(config);
|
|
55
|
+
(0, documents_1.setDocumentStoreDatabase)(config);
|
|
52
56
|
// Establish DB connections asynchronously (Postgres/Mongo + schema migrations).
|
|
53
57
|
// Until they're ready, HOLD incoming requests instead of letting them through —
|
|
54
58
|
// route handlers resolve projects/exports (e.g. the /menu-list export) against a
|
|
@@ -149,5 +153,7 @@ function createStreamByRouter(config) {
|
|
|
149
153
|
router.use((0, pipeline_1.pipelineRouter)(config));
|
|
150
154
|
router.use((0, worker_1.workerRouter)({ ...config, adapter }));
|
|
151
155
|
router.use((0, deliverable_1.deliverableRouter)({ ...config, adapter }));
|
|
156
|
+
router.use((0, document_1.documentRouter)(config));
|
|
157
|
+
router.use((0, projectResources_1.projectResourcesRouter)(config));
|
|
152
158
|
return router;
|
|
153
159
|
}
|
|
@@ -35,6 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.connectionRouter = connectionRouter;
|
|
37
37
|
const express_1 = require("express");
|
|
38
|
+
const workflowResources_1 = require("../../services/workflowResources");
|
|
38
39
|
const manager_1 = require("../../models/manager");
|
|
39
40
|
const auth_1 = require("../../utils/auth");
|
|
40
41
|
const connection_1 = require("../../services/connection");
|
|
@@ -88,7 +89,8 @@ function connectionRouter(config) {
|
|
|
88
89
|
...(description !== undefined && { description }),
|
|
89
90
|
...(token !== undefined && { token }),
|
|
90
91
|
});
|
|
91
|
-
|
|
92
|
+
await (0, workflowResources_1.addToWorkflow)(projectId, 'api', connection);
|
|
93
|
+
return res.status(201).json({ data: (0, sanitize_1.sanitizeConnection)(connection), connections: (0, sanitize_1.sanitizeConnections)((await Project.findOne({ _id: projectId }))?.connections) });
|
|
92
94
|
}
|
|
93
95
|
catch (err) {
|
|
94
96
|
res.status(500).json({ message: 'Failed to add connection', details: err.message });
|
|
@@ -178,7 +180,8 @@ function connectionRouter(config) {
|
|
|
178
180
|
return res.status(404).json({ message: 'Connection not found' });
|
|
179
181
|
}
|
|
180
182
|
await (0, connection_1.deleteConnection)(config, projectId, connectionId);
|
|
181
|
-
|
|
183
|
+
await (0, workflowResources_1.removeFromWorkflow)(projectId, 'api', connectionId);
|
|
184
|
+
return res.status(200).json({ message: 'Connection deleted successfully.', connections: (0, sanitize_1.sanitizeConnections)((await Project.findOne({ _id: projectId }))?.connections) });
|
|
182
185
|
}
|
|
183
186
|
catch (err) {
|
|
184
187
|
res.status(500).json({ message: 'Failed to delete connection', details: err.message });
|