@elevasis/sdk 1.50.0 → 1.52.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/dist/{chunk-MGZZ4HL4.js → chunk-T6DTAP2U.js} +191 -64
- package/dist/{chunk-YJDXRHNP.js → chunk-XC57JNMA.js} +79 -19
- package/dist/cli.cjs +183 -44
- package/dist/index.d.ts +248 -1
- package/dist/index.js +1 -1
- package/dist/test-utils/index.d.ts +13 -2
- package/dist/test-utils/index.js +16 -3
- package/dist/worker/index.d.ts +37 -1
- package/dist/worker/index.js +2 -2
- package/package.json +4 -4
- package/reference/_navigation.md +1 -2
- package/reference/_reference-manifest.json +0 -14
- package/reference/packages/core/src/organization-model/readiness/README.md +20 -1
- package/reference/packages/ui/src/hooks/README.md +22 -23
- package/reference/scaffold/recipes/extend-content.md +260 -12
- package/reference/sdk/cli.mdx +16 -3
- package/reference/ui/exports.mdx +0 -1
package/dist/test-utils/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { executeWorkflow } from '../chunk-
|
|
2
|
-
import { validateDeploymentSpec, validateRelationships } from '../chunk-
|
|
1
|
+
import { executeWorkflow } from '../chunk-T6DTAP2U.js';
|
|
2
|
+
import { validateDeploymentSpec, validateRelationships } from '../chunk-XC57JNMA.js';
|
|
3
3
|
import '../chunk-VYWGWJRW.js';
|
|
4
4
|
import { vi } from 'vitest';
|
|
5
5
|
|
|
@@ -341,6 +341,19 @@ var createMockGoogleSheets = (_credential, overrides) => createMockAdapter(
|
|
|
341
341
|
],
|
|
342
342
|
overrides
|
|
343
343
|
);
|
|
344
|
+
var createMockInstagram = (_credential, overrides) => createMockAdapter(
|
|
345
|
+
[
|
|
346
|
+
"createMediaContainer",
|
|
347
|
+
"createCarouselContainer",
|
|
348
|
+
"publishContainer",
|
|
349
|
+
"getContainerStatus",
|
|
350
|
+
"getMediaPermalink",
|
|
351
|
+
"getPublishingLimit",
|
|
352
|
+
"getMediaInsights",
|
|
353
|
+
"refreshToken"
|
|
354
|
+
],
|
|
355
|
+
overrides
|
|
356
|
+
);
|
|
344
357
|
var createMockInstantly = (_credential, overrides) => createMockAdapter(
|
|
345
358
|
[
|
|
346
359
|
"sendReply",
|
|
@@ -390,4 +403,4 @@ var createMockAnymailfinder = (_credential, overrides) => createMockAdapter(
|
|
|
390
403
|
);
|
|
391
404
|
var createMockMillionVerifier = (_credential, overrides) => createMockAdapter(["verifyEmail", "checkCredits"], overrides);
|
|
392
405
|
|
|
393
|
-
export { assertResourceRegistry, createMockAnymailfinder, createMockApify, createMockAttio, createMockDropbox, createMockGmail, createMockGoogleSheets, createMockInstantly, createMockMillionVerifier, createMockResend, createMockSignatureApi, createMockStripe, createMockTomba, mockAcqDb, mockApproval, mockArtifacts, mockContent, mockCrm, mockEmail, mockExecution, mockList, mockLlm, mockNotifications, mockPdf, mockProjects, mockScheduler, mockStorage, runLinearWorkflow, runWorkflow };
|
|
406
|
+
export { assertResourceRegistry, createMockAnymailfinder, createMockApify, createMockAttio, createMockDropbox, createMockGmail, createMockGoogleSheets, createMockInstagram, createMockInstantly, createMockMillionVerifier, createMockResend, createMockSignatureApi, createMockStripe, createMockTomba, mockAcqDb, mockApproval, mockArtifacts, mockContent, mockCrm, mockEmail, mockExecution, mockList, mockLlm, mockNotifications, mockPdf, mockProjects, mockScheduler, mockStorage, runLinearWorkflow, runWorkflow };
|
package/dist/worker/index.d.ts
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
import { AttioToolMap, ApifyToolMap, ClickUpToolMap, DropboxToolMap, GmailToolMap, GoogleSheetsToolMap, InstagramToolMap, InstantlyToolMap, MillionVerifierToolMap, AnymailfinderToolMap, TombaToolMap, ResendToolMap, SignatureApiToolMap, StripeToolMap, SchedulerToolMap, LLMGenerateRequest, LLMModel, LLMGenerateResponse, StorageToolMap, NotificationSDKInput, NotificationToolMap, LeadToolMap, ProjectsToolMap, CrmToolMap, ListToolMap, ArtifactsToolMap, ContentToolMap, PdfToolMap, ApprovalToolMap, ExecutionToolMap, EmailToolMap, WorkflowDefinition, WorkflowConfig, ListBuilderStep, LeadGenStageValidators, ResourceStatus, DeploymentSpec } from '@elevasis/sdk';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Platform Tool Proxy (Worker Side)
|
|
6
|
+
*
|
|
7
|
+
* Provides platform.call() for external developers to invoke platform tools
|
|
8
|
+
* from within worker threads. Communicates with the parent process via
|
|
9
|
+
* postMessage() -- the parent dispatches to the real service layer.
|
|
10
|
+
*/
|
|
11
|
+
/** Token usage metadata returned for tool calls with cost accounting. */
|
|
12
|
+
interface TokenUsage {
|
|
13
|
+
inputTokens: number;
|
|
14
|
+
outputTokens: number;
|
|
15
|
+
cost?: number;
|
|
16
|
+
model?: string;
|
|
17
|
+
}
|
|
4
18
|
/** Resolved credential returned by platform.getCredential() */
|
|
5
19
|
interface PlatformCredential {
|
|
6
20
|
provider: string;
|
|
@@ -34,6 +48,28 @@ declare const platform: {
|
|
|
34
48
|
params?: unknown;
|
|
35
49
|
credential?: string;
|
|
36
50
|
}): Promise<unknown>;
|
|
51
|
+
/**
|
|
52
|
+
* Call a platform tool and also surface any `usage` (token/cost) metadata the parent attached
|
|
53
|
+
* to the response -- e.g. the `llm` tool's real provider usage from the API-side
|
|
54
|
+
* `dispatchToolCall` in `tool-dispatcher.ts`. Bare `result` is unchanged from `call()`; `usage` is `undefined` whenever the
|
|
55
|
+
* parent's response didn't carry one, exactly like today's `call()` behavior for that result.
|
|
56
|
+
*
|
|
57
|
+
* @param options.tool - Tool name (e.g., 'llm')
|
|
58
|
+
* @param options.method - Method name (e.g., 'generate')
|
|
59
|
+
* @param options.params - Method parameters
|
|
60
|
+
* @param options.credential - Credential name (required for integration tools)
|
|
61
|
+
* @returns Promise resolving to `{ result, usage }`
|
|
62
|
+
* @throws PlatformToolError on failure (with code and retryable fields)
|
|
63
|
+
*/
|
|
64
|
+
callWithUsage(options: {
|
|
65
|
+
tool: string;
|
|
66
|
+
method: string;
|
|
67
|
+
params?: unknown;
|
|
68
|
+
credential?: string;
|
|
69
|
+
}): Promise<{
|
|
70
|
+
result: unknown;
|
|
71
|
+
usage?: TokenUsage;
|
|
72
|
+
}>;
|
|
37
73
|
/**
|
|
38
74
|
* Request raw credential access from the platform.
|
|
39
75
|
*
|
|
@@ -1128,7 +1164,7 @@ declare function toContentMetrics(insights: InstagramInsights): ContentMetrics;
|
|
|
1128
1164
|
* Parent -> Worker: { type: 'execute', resourceId, executionId, input, organizationId?, organizationName?,
|
|
1129
1165
|
* sessionId?, sessionTurnNumber?, sessionMemory?, conversationHistory?,
|
|
1130
1166
|
* parentExecutionId?, executionDepth }
|
|
1131
|
-
* Worker -> Parent: { type: 'result', status, output?, memorySnapshot?, error?, logs, metrics: { durationMs } }
|
|
1167
|
+
* Worker -> Parent: { type: 'result', status, output?, stopReason?, hasSpoken?, memorySnapshot?, error?, logs, metrics: { durationMs } }
|
|
1132
1168
|
*
|
|
1133
1169
|
* Parent -> Worker: { type: 'abort', reason? } (graceful abort before terminate;
|
|
1134
1170
|
* reason carries AbortSignal.reason
|
package/dist/worker/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { ListBuilderResultSchema, ListBuilderResultsSchema, PlatformToolError, acqDb, approval, artifacts, classifyPlatformToolError, content, createAdapter, createAnymailfinderAdapter, createApifyAdapter, createAttioAdapter, createCaptionGenerationWorkflow, createCaptureInstagramMetricsWorkflow, createClickUpAdapter, createDropboxAdapter, createGmailAdapter, createGoogleSheetsAdapter, createImageAnalysisWorkflow, createInstagramAdapter, createInstantlyAdapter, createMillionVerifierAdapter, createPublishInstagramWorkflow, createResendAdapter, createSignatureApiAdapter, createStripeAdapter, createTombaAdapter, crm, email, executeWorkflow, execution, generateHmacToken, list, listBuilderWorkflow, llm, notifications, pdf, platform, projects, scheduler, startWorker, storage, toContentMetrics } from '../chunk-
|
|
2
|
-
import '../chunk-
|
|
1
|
+
export { ListBuilderResultSchema, ListBuilderResultsSchema, PlatformToolError, acqDb, approval, artifacts, classifyPlatformToolError, content, createAdapter, createAnymailfinderAdapter, createApifyAdapter, createAttioAdapter, createCaptionGenerationWorkflow, createCaptureInstagramMetricsWorkflow, createClickUpAdapter, createDropboxAdapter, createGmailAdapter, createGoogleSheetsAdapter, createImageAnalysisWorkflow, createInstagramAdapter, createInstantlyAdapter, createMillionVerifierAdapter, createPublishInstagramWorkflow, createResendAdapter, createSignatureApiAdapter, createStripeAdapter, createTombaAdapter, crm, email, executeWorkflow, execution, generateHmacToken, list, listBuilderWorkflow, llm, notifications, pdf, platform, projects, scheduler, startWorker, storage, toContentMetrics } from '../chunk-T6DTAP2U.js';
|
|
2
|
+
import '../chunk-XC57JNMA.js';
|
|
3
3
|
import '../chunk-VYWGWJRW.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elevasis/sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.52.0",
|
|
4
4
|
"description": "SDK for building Elevasis organization resources",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -63,9 +63,9 @@
|
|
|
63
63
|
"typescript": "5.9.2",
|
|
64
64
|
"vitest": "^3.2.4",
|
|
65
65
|
"zod": "^4.1.0",
|
|
66
|
-
"@repo/core": "0.
|
|
67
|
-
"@repo/
|
|
68
|
-
"@repo/
|
|
66
|
+
"@repo/core": "0.69.0",
|
|
67
|
+
"@repo/typescript-config": "0.0.0",
|
|
68
|
+
"@repo/eslint-config": "0.0.0"
|
|
69
69
|
},
|
|
70
70
|
"scripts": {
|
|
71
71
|
"lint": "eslint src --max-warnings 0",
|
package/reference/_navigation.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
Auto-generated from the package reference manifests.
|
|
6
6
|
|
|
7
|
-
Package entries indexed:
|
|
7
|
+
Package entries indexed: 63.
|
|
8
8
|
|
|
9
9
|
## @elevasis/core / Auth
|
|
10
10
|
|
|
@@ -138,7 +138,6 @@ Package entries indexed: 64.
|
|
|
138
138
|
| Hooks Access | `packages/ui/src/hooks/README.md` | Published Access Model hook surface for checking route, section, and action access. | (not specified) |
|
|
139
139
|
| Hooks Delivery | `packages/ui/src/hooks/README.md` | Published delivery hooks for projects, milestones, tasks, and notes. | (not specified) |
|
|
140
140
|
| Hooks User Notes | `packages/ui/src/hooks/README.md` | Published user-notes query and mutation hooks used by shared Notes surfaces. | (not specified) |
|
|
141
|
-
| Transform Command View Data | `packages/ui/src/hooks/README.md` | Utility that transforms backend CommandViewData arrays into a unified frontend CommandViewGraph with nodes and edges. | (not specified) |
|
|
142
141
|
|
|
143
142
|
## @elevasis/ui / Provider
|
|
144
143
|
|
|
@@ -743,20 +743,6 @@
|
|
|
743
743
|
"referencePath": "packages/ui/src/hooks/README.md",
|
|
744
744
|
"publishedExportPath": "./dist/hooks/user-notes/index.js"
|
|
745
745
|
},
|
|
746
|
-
{
|
|
747
|
-
"packageName": "@elevasis/ui",
|
|
748
|
-
"packageDir": "packages/ui",
|
|
749
|
-
"subpath": "./hooks/operations/command-view/utils/transformCommandViewData",
|
|
750
|
-
"kind": "subpath",
|
|
751
|
-
"title": "Transform Command View Data",
|
|
752
|
-
"description": "Utility that transforms backend CommandViewData arrays into a unified frontend CommandViewGraph with nodes and edges.",
|
|
753
|
-
"group": "Hooks",
|
|
754
|
-
"order": 3,
|
|
755
|
-
"sourcePath": "packages/ui/src/hooks/operations/command-view/utils/transformCommandViewData.ts",
|
|
756
|
-
"docPath": "packages/ui/src/hooks/README.md",
|
|
757
|
-
"referencePath": "packages/ui/src/hooks/README.md",
|
|
758
|
-
"publishedExportPath": "./dist/hooks/operations/command-view/utils/transformCommandViewData.js"
|
|
759
|
-
},
|
|
760
746
|
{
|
|
761
747
|
"packageName": "@elevasis/ui",
|
|
762
748
|
"packageDir": "packages/ui",
|
|
@@ -31,10 +31,29 @@ import {
|
|
|
31
31
|
type ReadinessProfileValidator,
|
|
32
32
|
type ReadinessOntologyIndex,
|
|
33
33
|
type ReadinessInterfaceMarkerResolver,
|
|
34
|
-
type SystemInterfaceMarker
|
|
34
|
+
type SystemInterfaceMarker,
|
|
35
|
+
// What to do about a failure, shared by the API's 503 message and the UI alert
|
|
36
|
+
resolveReadinessRemedy,
|
|
37
|
+
describeReadinessFailure,
|
|
38
|
+
isResolutionIssue,
|
|
39
|
+
REDEPLOY_REMEDY,
|
|
40
|
+
RESOLUTION_ISSUE_CODES,
|
|
41
|
+
type ResolutionIssueCode
|
|
35
42
|
} from '@elevasis/core/organization-model/readiness'
|
|
36
43
|
```
|
|
37
44
|
|
|
45
|
+
## Remedies live here, not at each consumer
|
|
46
|
+
|
|
47
|
+
`remedies.ts` maps a readiness issue to the one sentence telling an operator what to do about it,
|
|
48
|
+
keyed exhaustively over the family union so a new family cannot be added without deciding its answer.
|
|
49
|
+
Both consumers read it: `createInterfaceReadinessApiError` builds the 503's message from
|
|
50
|
+
`describeReadinessFailure`, and `@repo/ui`'s `SystemReadinessAlert` renders `resolveReadinessRemedy`.
|
|
51
|
+
|
|
52
|
+
It keys on the issue's `code` before its `family` because the two can disagree.
|
|
53
|
+
`MODEL_PARSE_FAILED` arrives under `SYSTEM_INTERFACE_INVALID`, whose family-level answer is "fix your
|
|
54
|
+
declaration" — and the declaration is fine; the stored snapshot is what could not be read. Keying on
|
|
55
|
+
family alone is exactly the bug this module was extracted to prevent.
|
|
56
|
+
|
|
38
57
|
This subpath exists so the extension point can be reached without pulling in the rest of `@elevasis/core`. It points at the same module workspace consumers import, deliberately — the two surfaces cannot drift apart.
|
|
39
58
|
|
|
40
59
|
## Layering constraint
|
|
@@ -1,23 +1,22 @@
|
|
|
1
|
-
# Hooks
|
|
2
|
-
|
|
3
|
-
The hooks barrel is the published headless hook surface for the UI package.
|
|
4
|
-
|
|
5
|
-
## Grouped Areas
|
|
6
|
-
|
|
7
|
-
- Execution and workflow hooks
|
|
8
|
-
- Scheduling hooks
|
|
9
|
-
- Monitoring, observability, and notification hooks
|
|
10
|
-
- Session and SSE hooks
|
|
11
|
-
- Operations hooks, including command-view helpers
|
|
12
|
-
- Feature access, table state, and service helpers
|
|
13
|
-
- Acquisition and delivery hooks
|
|
14
|
-
|
|
15
|
-
## Published Subpaths
|
|
16
|
-
|
|
17
|
-
- `./hooks`
|
|
18
|
-
- `./hooks/delivery`
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
- This barrel is intentionally headless. It should not pull in the visual component layer.
|
|
1
|
+
# Hooks
|
|
2
|
+
|
|
3
|
+
The hooks barrel is the published headless hook surface for the UI package.
|
|
4
|
+
|
|
5
|
+
## Grouped Areas
|
|
6
|
+
|
|
7
|
+
- Execution and workflow hooks
|
|
8
|
+
- Scheduling hooks
|
|
9
|
+
- Monitoring, observability, and notification hooks
|
|
10
|
+
- Session and SSE hooks
|
|
11
|
+
- Operations hooks, including command-view helpers
|
|
12
|
+
- Feature access, table state, and service helpers
|
|
13
|
+
- Acquisition and delivery hooks
|
|
14
|
+
|
|
15
|
+
## Published Subpaths
|
|
16
|
+
|
|
17
|
+
- `./hooks`
|
|
18
|
+
- `./hooks/delivery`
|
|
19
|
+
|
|
20
|
+
## Notes
|
|
21
|
+
|
|
22
|
+
- This barrel is intentionally headless. It should not pull in the visual component layer.
|
|
@@ -51,23 +51,30 @@ A pipeline's step catalog is named `content:catalog/{pipelineId}-steps` and refe
|
|
|
51
51
|
| Add content nav or a content route | `CONTENT_ITEMS`, `ContentSidebar`, `ContentSidebarMiddle` | Same manifest/sidebar composition pattern as CRM and lead-gen. |
|
|
52
52
|
| Give producers a style guide or voice constraints | The generating workflow's own prompt | Content has no rules-document tier. Put the instructions where the model reads them. |
|
|
53
53
|
| Track where a piece was published | `createDistribution` / `updateDistribution` | One distribution row per platform per item. |
|
|
54
|
+
| Actually post an item to a platform | Section 6 — a publish panel of yours, over a prepared distribution row | Publishing is operating, so it is your screen. Instagram has a published workflow; do not copy it. |
|
|
54
55
|
| Pick source material for a new item | `createSourceAsset` / `getSourceAsset` / `listSourceAssets` / `updateSourceAsset` | Raw material a producer works from -- an uploaded episode, a transcript, a reference file. |
|
|
55
56
|
| Operate the pipeline from outside a browser | `elevasis-sdk content:*` | Read-only plus one write (`content:review`). Not how a workflow produces content -- see the boundary below. |
|
|
56
57
|
| Add a new persisted content column or table | Platform/API migration work, not just scaffold work | DB, core schemas/types, API service/handlers, hooks, docs, and scaffold contracts move together. |
|
|
57
58
|
|
|
58
59
|
## Published Content Surfaces
|
|
59
60
|
|
|
60
|
-
| Surface | Import from
|
|
61
|
-
| ---------------------------------------------------------------------------------------------------------------------- |
|
|
62
|
-
| `contentManifest`, `CONTENT_ITEMS`, `ContentSidebar`, `ContentSidebarMiddle`, `MyReviewQueuePanel` | `@elevasis/ui/features/content`
|
|
63
|
-
| `ContentOverviewPage`, `ContentItemsPage`, `ContentItemReviewPage`, `ContentPipelinesPage`, `ContentPipelineBoardPage` | `@elevasis/ui/features/content`
|
|
64
|
-
| `ContentDistributionsPage`, `ContentDistributionDetailPage` | `@elevasis/ui/features/content`
|
|
65
|
-
| `ContentReviewCard`, `ReviewActionBar`, `PayloadBody`, `AlternatesPanel`, `ProcessingStateStrip` | `@elevasis/ui/features/content`
|
|
66
|
-
| `useContentConfig`, `resolveContentStepResource` | `@elevasis/ui/features/content`
|
|
67
|
-
| `useContentItems`, `useContentItem`, `useContentItemAttempts`, `useUpdateContentItem`, `useReviewContentItem` | `@elevasis/ui/hooks`
|
|
68
|
-
| `useContentDistributions`, `useContentDistribution`, `useUpdateContentDistribution` | `@elevasis/ui/hooks`
|
|
69
|
-
| `useContentPipelineSummary` | `@elevasis/ui/hooks`
|
|
70
|
-
| `
|
|
61
|
+
| Surface | Import from | Use for |
|
|
62
|
+
| ---------------------------------------------------------------------------------------------------------------------- | ----------------------------------- | ------------------------------------------------ |
|
|
63
|
+
| `contentManifest`, `CONTENT_ITEMS`, `ContentSidebar`, `ContentSidebarMiddle`, `MyReviewQueuePanel` | `@elevasis/ui/features/content` | Feature registration and sidebar composition |
|
|
64
|
+
| `ContentOverviewPage`, `ContentItemsPage`, `ContentItemReviewPage`, `ContentPipelinesPage`, `ContentPipelineBoardPage` | `@elevasis/ui/features/content` | Shared pages you can route to or wrap |
|
|
65
|
+
| `ContentDistributionsPage`, `ContentDistributionDetailPage` | `@elevasis/ui/features/content` | Distribution surfaces |
|
|
66
|
+
| `ContentReviewCard`, `ReviewActionBar`, `PayloadBody`, `AlternatesPanel`, `ProcessingStateStrip` | `@elevasis/ui/features/content` | Review UI primitives for a custom review screen |
|
|
67
|
+
| `useContentConfig`, `resolveContentStepResource` | `@elevasis/ui/features/content` | Read the resolved pipeline/step catalogs from OM |
|
|
68
|
+
| `useContentItems`, `useContentItem`, `useContentItemAttempts`, `useUpdateContentItem`, `useReviewContentItem` | `@elevasis/ui/hooks` | Item data access and review mutations |
|
|
69
|
+
| `useContentDistributions`, `useContentDistribution`, `useUpdateContentDistribution` | `@elevasis/ui/hooks` | Distribution data access |
|
|
70
|
+
| `useContentPipelineSummary` | `@elevasis/ui/hooks` | Pipeline roll-ups |
|
|
71
|
+
| `ContentWorkspaceShell`, `ContentAnalysisStepPanel`, `ContentStepPanelMap`, `ContentWorkspaceState` | `@elevasis/ui/features/content` | The workspace frame and its panel contract |
|
|
72
|
+
| `useExecuteAsync`, `useExecution`, `isExecutionInFlight`, `TERMINAL_EXECUTION_STATUSES` | `@elevasis/ui/hooks` | Start a publishing workflow and watch it finish |
|
|
73
|
+
| `useCreateContentDistribution`, `useUpdateContentDistribution` | `@elevasis/ui/hooks` | Prepare and record a post |
|
|
74
|
+
| `content` | `@elevasis/sdk/worker` | Workflow-side content adapter |
|
|
75
|
+
| `createPublishInstagramWorkflow`, `createInstagramAdapter` | `@elevasis/sdk/worker` | Post to Instagram from a workflow |
|
|
76
|
+
| `createMockInstagram` | `@elevasis/sdk/test-utils` | Test a publish pipeline without a real account |
|
|
77
|
+
| `defineContentPipeline`, `defineContentSystem` | `@elevasis/core/organization-model` | Declare a pipeline and derive the System block |
|
|
71
78
|
|
|
72
79
|
Read the generated contracts before changing typed boundaries:
|
|
73
80
|
|
|
@@ -418,13 +425,254 @@ rendering "Pipelines" here, and nothing fails to tell you.
|
|
|
418
425
|
Replace `contentManifest` with `customContentManifest` in the local `SYSTEM_MANIFESTS` array and add
|
|
419
426
|
the matching route under `ui/src/routes/content/`.
|
|
420
427
|
|
|
421
|
-
## 6.
|
|
428
|
+
## 6. Send It Out
|
|
429
|
+
|
|
430
|
+
Sections 1 through 5 get an item made and reviewed. This one covers the last step of a pipeline
|
|
431
|
+
that publishes: turning a finished item into a post on a platform.
|
|
432
|
+
|
|
433
|
+
Publishing is **operating**, so by the section 5 contract it is yours: a panel on your workspace,
|
|
434
|
+
not a shared page. The shared distribution pages display what went out and never write. What the
|
|
435
|
+
platform gives you is the distribution row, a way to start a workflow and watch it, and — for
|
|
436
|
+
Instagram — the publishing workflow itself.
|
|
437
|
+
|
|
438
|
+
### A panel is registered against a step key
|
|
439
|
+
|
|
440
|
+
`ContentWorkspaceShell` walks the pipeline from the deployed model and renders whichever panel
|
|
441
|
+
your map names for the active step. The map is keyed by **step key**, not by any convention on the
|
|
442
|
+
component:
|
|
443
|
+
|
|
444
|
+
```tsx
|
|
445
|
+
import { ContentAnalysisStepPanel, ContentWorkspaceShell } from '@elevasis/ui/features/content'
|
|
446
|
+
import type { ContentStepPanelMap } from '@elevasis/ui/features/content'
|
|
447
|
+
|
|
448
|
+
interface PostDraft {
|
|
449
|
+
platform: string | null
|
|
450
|
+
publishExecutionId: string | null
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
const panels: ContentStepPanelMap<PostDraft> = {
|
|
454
|
+
analysis: ContentAnalysisStepPanel,
|
|
455
|
+
caption: CaptionStepPanel,
|
|
456
|
+
publish: PublishStepPanel
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
export function WritePostPage() {
|
|
460
|
+
return (
|
|
461
|
+
<ContentWorkspaceShell
|
|
462
|
+
panels={panels}
|
|
463
|
+
initialDraft={{ platform: null, publishExecutionId: null }}
|
|
464
|
+
title="Write a Post"
|
|
465
|
+
/>
|
|
466
|
+
)
|
|
467
|
+
}
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
Every panel is called with the same `ContentWorkspaceState<TDraft>` — the item, its body, the
|
|
471
|
+
distribution row, `refetchDistributions`, step navigation, and your own `draft`. Anything matching
|
|
472
|
+
that signature is a panel. A step key with no entry in the map is reported rather than rendering
|
|
473
|
+
nothing, which is the reason the map is explicit instead of derived from the key.
|
|
474
|
+
|
|
475
|
+
`ContentAnalysisStepPanel` is the one step panel published today, because it needs an item id and
|
|
476
|
+
nothing else. A publish panel is not shippable the same way: it reaches a credential, a status
|
|
477
|
+
vocabulary, and a workflow id, all of which are yours to choose.
|
|
478
|
+
|
|
479
|
+
### Prepare the distribution row before posting
|
|
480
|
+
|
|
481
|
+
A distribution is one row per platform per item, and it is what the publishing workflow reads. It
|
|
482
|
+
must exist, and carry its media, **before** anything goes out:
|
|
483
|
+
|
|
484
|
+
```tsx
|
|
485
|
+
const createDistribution = useCreateContentDistribution()
|
|
486
|
+
|
|
487
|
+
await createDistribution.mutateAsync({
|
|
488
|
+
contentItemId: item.id,
|
|
489
|
+
platform,
|
|
490
|
+
format: 'carousel',
|
|
491
|
+
status: 'pending',
|
|
492
|
+
// The caption as it will go out. The row is the record of the post, not a pointer at an
|
|
493
|
+
// item whose body may be edited afterwards.
|
|
494
|
+
adaptedBody: body.trim().length > 0 ? body : null,
|
|
495
|
+
mediaUrls: renderedEntries
|
|
496
|
+
})
|
|
497
|
+
refetchDistributions()
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
The row freezing what goes out is the point: re-cropping an image after the fact must not quietly
|
|
501
|
+
change what the record says was posted. The corollary is that a row prepared before the caption was
|
|
502
|
+
written holds a stale caption — write the current body onto it at post time, while `publishedAt` is
|
|
503
|
+
still null and nothing has gone out yet.
|
|
504
|
+
|
|
505
|
+
### Reaching a publishing workflow
|
|
506
|
+
|
|
507
|
+
Start it async and watch it. Staging a carousel is one upload per image plus polling until the
|
|
508
|
+
platform reports each container finished, which is far longer than a request a page can hold open:
|
|
509
|
+
|
|
510
|
+
```tsx
|
|
511
|
+
const startPublish = useExecuteAsync()
|
|
512
|
+
const publishExecution = useExecution(publishWorkflowId, draft.publishExecutionId ?? '')
|
|
513
|
+
|
|
514
|
+
const started = await startPublish.mutateAsync({
|
|
515
|
+
resourceId: publishWorkflowId,
|
|
516
|
+
resourceType: 'workflow',
|
|
517
|
+
input: { distributionId: distribution.id }
|
|
518
|
+
})
|
|
519
|
+
setDraft({ publishExecutionId: started.executionId })
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
**Name the workflow; do not ask the model which one it is.** `resolveContentStepResource` returns
|
|
523
|
+
the first resource whose ontology claims the step's action, which is a coin flip once more than one
|
|
524
|
+
does. A workflow id is a decision your app makes — pass it as a prop or read it from your own
|
|
525
|
+
config.
|
|
526
|
+
|
|
527
|
+
**Use `isExecutionInFlight` for "is it still going", not `status === 'running'`.** `warning` is a
|
|
528
|
+
terminal status the platform picks for a run that succeeded while emitting warn-level logs, so a
|
|
529
|
+
`running` test treats a finished run as still in flight and a `completed`-only test never notices
|
|
530
|
+
it finished. Both are exported from `@elevasis/ui/hooks`:
|
|
531
|
+
|
|
532
|
+
```tsx
|
|
533
|
+
import { isExecutionInFlight, TERMINAL_EXECUTION_STATUSES } from '@elevasis/ui/hooks'
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
`useExecution` already polls on that predicate, so a panel needs no poll of its own. One browser
|
|
537
|
+
caveat when you go to verify it: React Query pauses `refetchInterval` while `document.hidden`, so a
|
|
538
|
+
backgrounded tab measures zero requests against correct code.
|
|
539
|
+
|
|
540
|
+
### Instagram: use the published workflow, do not copy it
|
|
541
|
+
|
|
542
|
+
`createPublishInstagramWorkflow` takes a credential name and returns a registered workflow. That is
|
|
543
|
+
the whole of what is project-specific about posting to Instagram:
|
|
544
|
+
|
|
545
|
+
<!-- doc-snippet:skip: illustrative excerpt -- a project-local registration module -->
|
|
546
|
+
|
|
547
|
+
```ts
|
|
548
|
+
import { createPublishInstagramWorkflow } from '@elevasis/sdk/worker'
|
|
549
|
+
|
|
550
|
+
export const publishInstagramWorkflow = createPublishInstagramWorkflow({
|
|
551
|
+
credentialName: 'acme-instagram',
|
|
552
|
+
resourceId: 'cnt-publish-instagram-workflow'
|
|
553
|
+
})
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
The credential holds `{ accessToken, igUserId }` — the account id rides along with the token, so no
|
|
557
|
+
caller carries it. Given a `distributionId` the workflow signs each image, builds the containers,
|
|
558
|
+
publishes, and writes the returned post id and permalink back onto the same row. It does not move
|
|
559
|
+
the item's status: which status means "finished" is resolved against your own model by the surface
|
|
560
|
+
that owns the item, and deciding it twice is two answers that can disagree.
|
|
561
|
+
|
|
562
|
+
**It refuses rather than repairs, and that is why it is not yours to copy.** Three checks run before
|
|
563
|
+
anything reaches Instagram, each guarding an action that cannot be taken back:
|
|
564
|
+
|
|
565
|
+
| Refusal | Why it is a refusal |
|
|
566
|
+
| ---------------------------------------- | --------------------------------------------------------------------------- |
|
|
567
|
+
| the row's `platform` is not `instagram` | posting a row prepared for somewhere else posts the wrong thing |
|
|
568
|
+
| the row already carries `platformPostId` | only a real publish writes that field, so posting again duplicates the post |
|
|
569
|
+
| the row already carries `publishedAt` | someone recorded this as posted; posting now would post it twice |
|
|
570
|
+
| the row has no `mediaUrls` | there is nothing to post — render first |
|
|
571
|
+
|
|
572
|
+
To post again, create a new distribution. Nothing clears these.
|
|
573
|
+
|
|
574
|
+
A failed execution therefore generally means **nothing was posted**, and its own error message is
|
|
575
|
+
more specific than anything a panel could write — show `execution.error` rather than a generic line.
|
|
576
|
+
|
|
577
|
+
### Posting by hand
|
|
578
|
+
|
|
579
|
+
Not every platform has a workflow behind it, and the manual path needs no credential at all.
|
|
580
|
+
Record it on the same row:
|
|
581
|
+
|
|
582
|
+
```tsx
|
|
583
|
+
await updateDistribution.mutateAsync({
|
|
584
|
+
distributionId: distribution.id,
|
|
585
|
+
updates: {
|
|
586
|
+
status: 'published',
|
|
587
|
+
publishMethod: 'manual',
|
|
588
|
+
publishedAt: new Date().toISOString(),
|
|
589
|
+
...(isUsableUrl ? { platformUrl: typedUrl } : {})
|
|
590
|
+
}
|
|
591
|
+
})
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
`publishedAt` set with no `platformPostId` is the manual shape — do not invent an id no platform
|
|
595
|
+
returned. When you later ask how a post went out, test against `'manual'` rather than against an
|
|
596
|
+
API value: a publishing workflow writes the platform it posted through (`'instagram-api'`), so
|
|
597
|
+
"was this done by hand" is the question with one stable answer.
|
|
598
|
+
|
|
599
|
+
### Testing a publish pipeline
|
|
600
|
+
|
|
601
|
+
Mock the integration; do not point a test at a real account. `createMockInstagram` covers all eight
|
|
602
|
+
adapter methods:
|
|
603
|
+
|
|
604
|
+
<!-- doc-snippet:skip: illustrative excerpt -- vitest usage inside a project's own suite -->
|
|
605
|
+
|
|
606
|
+
```ts
|
|
607
|
+
import { createMockInstagram } from '@elevasis/sdk/test-utils'
|
|
608
|
+
|
|
609
|
+
const instagram = createMockInstagram('acme-instagram', {
|
|
610
|
+
// A publish sequence polls until the container reports FINISHED. Un-overridden, it never does.
|
|
611
|
+
getContainerStatus: ({ containerId }) => ({ containerId, statusCode: 'FINISHED', status: 'Finished' }),
|
|
612
|
+
publishContainer: { mediaId: 'media-1' }
|
|
613
|
+
})
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
## 7. Mark the System API-Ready
|
|
422
617
|
|
|
423
618
|
`content.apiInterface.lifecycle` goes `active` only when the System's scoped resources, `content:object/item`, and the pipeline/step/status catalogs are all in place. Two failure modes are worth knowing before you flip it:
|
|
424
619
|
|
|
425
620
|
- An **empty** `apiInterface.resourceIds` array throws rather than opting out. Omitting the whole `apiInterface` block is the opt-out; a `resourceIds: []` stub is the anomaly.
|
|
426
621
|
- Readiness is derived from a **deployed** snapshot. A model edit that has not been redeployed produces a stale-snapshot 503 that looks like a code bug. Redeploy before debugging.
|
|
427
622
|
|
|
623
|
+
### Or derive the whole block with `defineContentSystem`
|
|
624
|
+
|
|
625
|
+
`defineContentPipeline` (section 1) removes the per-pipeline duplication. What is left is the
|
|
626
|
+
surrounding boilerplate every content adopter writes identically: the `apiInterface` block, its
|
|
627
|
+
`readinessContract`, the `content:object/item` object type, and the status catalog. `defineContentSystem`
|
|
628
|
+
takes the pipelines and derives all four.
|
|
629
|
+
|
|
630
|
+
<!-- doc-snippet:skip: illustrative excerpt -- references project-local pipeline declarations -->
|
|
631
|
+
|
|
632
|
+
```ts
|
|
633
|
+
import { defineContentPipeline, defineContentSystem } from '@elevasis/core/organization-model'
|
|
634
|
+
|
|
635
|
+
const linkedInPost = defineContentPipeline({
|
|
636
|
+
systemPath: 'content',
|
|
637
|
+
id: 'linkedin-post',
|
|
638
|
+
label: 'LinkedIn Post',
|
|
639
|
+
workspaceRoute: '/content/write',
|
|
640
|
+
steps: [{ key: 'draft', actor: 'agent', review: 'required', resource: saveContentBatch }]
|
|
641
|
+
})
|
|
642
|
+
|
|
643
|
+
export const contentSystem = defineContentSystem({
|
|
644
|
+
systemPath: 'content',
|
|
645
|
+
resourceIds: ['save-content-batch', 'publish-linkedin'],
|
|
646
|
+
pipelines: [linkedInPost],
|
|
647
|
+
item: { label: 'Post', description: 'One LinkedIn post, from idea to distribution.' },
|
|
648
|
+
statuses: [
|
|
649
|
+
{ key: 'draft', label: 'Written', order: 10, semanticClass: 'active' },
|
|
650
|
+
{ key: 'posted', label: 'Posted', order: 20, semanticClass: 'completed' }
|
|
651
|
+
]
|
|
652
|
+
})
|
|
653
|
+
|
|
654
|
+
// Inside the content System:
|
|
655
|
+
// apiInterface: contentSystem.apiInterface
|
|
656
|
+
// ontology: { ...contentSystem.objectTypes, ...contentSystem.catalogTypes, ...yourOwn }
|
|
657
|
+
```
|
|
658
|
+
|
|
659
|
+
It emits `apiInterface` (with `readinessProfile: 'content.api'` and a `readinessContract` listing
|
|
660
|
+
the pipeline, every step catalog, and the status catalog), `objectTypes` carrying
|
|
661
|
+
`content:object/item`, and `catalogTypes` carrying the pipeline, step, and status catalogs. Your own
|
|
662
|
+
object types, action types, and per-adopter catalogs — pillar, platform, source-asset kind — stay
|
|
663
|
+
hand-authored alongside them.
|
|
664
|
+
|
|
665
|
+
**The contract is unchanged; only the boilerplate is gone.** Content is validated against a
|
|
666
|
+
declared `readinessContract` rather than a platform validator, and that is deliberate: a CRM
|
|
667
|
+
pipeline is a CRM pipeline, so platform code can hardcode what to check, while a content pipeline is
|
|
668
|
+
whatever its adopter declares. Making it built-in would hardcode one step catalog for every project.
|
|
669
|
+
`defineContentSystem` writes the contract for you; it does not remove it.
|
|
670
|
+
|
|
671
|
+
**`lifecycle` defaults to `'active'`, so calling this helper is itself the decision to serve the
|
|
672
|
+
API.** Pass `lifecycle: 'draft'` while the pipelines are declared but not yet served. The opt-out
|
|
673
|
+
above — omitting `apiInterface` entirely — means not calling `defineContentSystem` for that block at
|
|
674
|
+
all; there is no way to call it and get no marker.
|
|
675
|
+
|
|
428
676
|
## Verify
|
|
429
677
|
|
|
430
678
|
Run the checks for the surfaces you touched:
|
package/reference/sdk/cli.mdx
CHANGED
|
@@ -520,7 +520,7 @@ Manage credentials for your organization. Credentials store API keys and secrets
|
|
|
520
520
|
|
|
521
521
|
```
|
|
522
522
|
elevasis-sdk creds list
|
|
523
|
-
elevasis-sdk creds create --name <name> --type <type>
|
|
523
|
+
elevasis-sdk creds create --name <name> --type <type> --value <json>
|
|
524
524
|
elevasis-sdk creds update <name> --value <json>
|
|
525
525
|
elevasis-sdk creds rename <name> --to <newName>
|
|
526
526
|
elevasis-sdk creds delete <name> [--force]
|
|
@@ -539,20 +539,33 @@ elevasis-sdk creds delete <name> [--force]
|
|
|
539
539
|
| Flag | Description |
|
|
540
540
|
| ----------------- | ------------------------------------------------------------------------------- |
|
|
541
541
|
| `--name <name>` | Credential name: lowercase letters, digits, and hyphens only (create: required) |
|
|
542
|
-
| `--type <type>` | Credential type
|
|
542
|
+
| `--type <type>` | Credential type (create: required). See the table below |
|
|
543
543
|
| `--value <json>` | Credential value as a JSON string (create and update: required) |
|
|
544
544
|
| `--to <newName>` | New name (rename: required) |
|
|
545
545
|
| `--force` | Skip confirmation prompt (delete) |
|
|
546
546
|
| `--prod` | Target production (overrides `NODE_ENV=development`) |
|
|
547
547
|
| `--api-url <url>` | Override the API base URL |
|
|
548
548
|
|
|
549
|
-
|
|
549
|
+
**Keep long-lived secrets out of `--value`.** A value passed literally survives in shell history and in the transcript of whatever ran the command. Write the JSON to a file and pass `--value @json:tmp/credential.json` instead -- the `@json:` prefix works on any flag of any `elevasis-sdk` command, resolves relative paths against the project root, and expands inside the CLI process, so the secret never appears in the invocation. Delete the file afterwards.
|
|
550
|
+
|
|
551
|
+
**Credential types.** The accepted values are the platform's own `CredentialTypeSchema`, minus `oauth`:
|
|
552
|
+
|
|
553
|
+
| Type | Shape |
|
|
554
|
+
| ---------------- | -------------------------------------------------- |
|
|
555
|
+
| `api-key` | Single-field API key |
|
|
556
|
+
| `api-key-secret` | Key and secret pair |
|
|
557
|
+
| `webhook-secret` | Webhook signing secret |
|
|
558
|
+
| `clickup` | ClickUp personal token |
|
|
559
|
+
| `instagram` | `{ accessToken, igUserId }` for Content Publishing |
|
|
560
|
+
|
|
561
|
+
OAuth credentials cannot be created through the CLI -- they need a `provider`, which the external create route does not accept, and they require the Command Center's browser OAuth flow. See [Command Center](deployment/command-center.mdx#credentials).
|
|
550
562
|
|
|
551
563
|
**Examples:**
|
|
552
564
|
|
|
553
565
|
```bash
|
|
554
566
|
elevasis-sdk creds list
|
|
555
567
|
elevasis-sdk creds create --name openai-key --type api-key --value '{"key":"sk-proj-***"}'
|
|
568
|
+
elevasis-sdk creds create --name my-instagram --type instagram --value @json:tmp/ig.json --prod
|
|
556
569
|
elevasis-sdk creds update openai-key --value '{"key":"sk-proj-new"}'
|
|
557
570
|
elevasis-sdk creds rename openai-key --to openai-prod-key
|
|
558
571
|
elevasis-sdk creds delete openai-prod-key --force
|
package/reference/ui/exports.mdx
CHANGED
|
@@ -56,7 +56,6 @@ description: "Auto-generated catalog of all published @elevasis/ui subpath expor
|
|
|
56
56
|
| `@elevasis/ui/provider/ElevasisServiceContext` | Elevasis Service Context | Provider | Standalone service context and provider that supplies apiRequest, organizationId, and isReady to child components. |
|
|
57
57
|
| `@elevasis/ui/hooks/delivery` | Hooks Delivery | Hooks | Published delivery hooks for projects, milestones, tasks, and notes. |
|
|
58
58
|
| `@elevasis/ui/hooks/user-notes` | Hooks User Notes | Hooks | Published user-notes query and mutation hooks used by shared Notes surfaces. |
|
|
59
|
-
| `@elevasis/ui/hooks/operations/command-view/utils/transformCommandViewData` | Transform Command View Data | Hooks | Utility that transforms backend CommandViewData arrays into a unified frontend CommandViewGraph with nodes and edges. |
|
|
60
59
|
| `@elevasis/ui/test-utils` | Test Utils | Testing | Published rendering helpers, auth mocks, MSW handlers, and test provider utilities. |
|
|
61
60
|
| `@elevasis/ui/test-utils/setup` | Test Utils Setup | Testing | Vitest setup file for UI consumers using browser mocks and MSW. |
|
|
62
61
|
| `@elevasis/ui/test-utils/setup-integration` | Test Utils Integration Setup | Testing | Vitest setup file for integration tests that avoid MSW and use real network boundaries. |
|