primitive-admin 1.1.0-alpha.4 → 1.1.0-alpha.41
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/README.md +325 -31
- package/assets/skill/skills/primitive-platform/SKILL.md +281 -0
- package/dist/bin/primitive.d.ts +2 -0
- package/dist/bin/primitive.js +268 -16
- package/dist/bin/primitive.js.map +1 -1
- package/dist/src/commands/admins.d.ts +2 -0
- package/dist/src/commands/admins.js +123 -18
- package/dist/src/commands/admins.js.map +1 -1
- package/dist/src/commands/analytics.d.ts +2 -0
- package/dist/src/commands/analytics.js +464 -55
- package/dist/src/commands/analytics.js.map +1 -1
- package/dist/src/commands/apps.d.ts +2 -0
- package/dist/src/commands/apps.js +55 -13
- package/dist/src/commands/apps.js.map +1 -1
- package/dist/src/commands/auth.d.ts +2 -0
- package/dist/src/commands/auth.js +177 -7
- package/dist/src/commands/auth.js.map +1 -1
- package/dist/src/commands/blob-buckets.d.ts +2 -0
- package/dist/src/commands/blob-buckets.js +422 -0
- package/dist/src/commands/blob-buckets.js.map +1 -0
- package/dist/src/commands/catalog.d.ts +2 -0
- package/dist/src/commands/catalog.js +34 -35
- package/dist/src/commands/catalog.js.map +1 -1
- package/dist/src/commands/collection-type-configs.d.ts +2 -0
- package/dist/src/commands/collection-type-configs.js +178 -0
- package/dist/src/commands/collection-type-configs.js.map +1 -0
- package/dist/src/commands/collections.d.ts +2 -0
- package/dist/src/commands/collections.js +515 -0
- package/dist/src/commands/collections.js.map +1 -0
- package/dist/src/commands/comparisons.d.ts +2 -0
- package/dist/src/commands/comparisons.js +6 -6
- package/dist/src/commands/comparisons.js.map +1 -1
- package/dist/src/commands/cron-triggers.d.ts +2 -0
- package/dist/src/commands/cron-triggers.js +357 -0
- package/dist/src/commands/cron-triggers.js.map +1 -0
- package/dist/src/commands/database-types.d.ts +2 -0
- package/dist/src/commands/database-types.js +471 -0
- package/dist/src/commands/database-types.js.map +1 -0
- package/dist/src/commands/databases.d.ts +2 -0
- package/dist/src/commands/databases.js +1462 -83
- package/dist/src/commands/databases.js.map +1 -1
- package/dist/src/commands/documents.d.ts +2 -0
- package/dist/src/commands/documents.js +518 -11
- package/dist/src/commands/documents.js.map +1 -1
- package/dist/src/commands/email-templates.d.ts +2 -0
- package/dist/src/commands/email-templates.js +281 -0
- package/dist/src/commands/email-templates.js.map +1 -0
- package/dist/src/commands/env.d.ts +12 -0
- package/dist/src/commands/env.js +260 -0
- package/dist/src/commands/env.js.map +1 -0
- package/dist/src/commands/group-type-configs.d.ts +2 -0
- package/dist/src/commands/group-type-configs.js +181 -0
- package/dist/src/commands/group-type-configs.js.map +1 -0
- package/dist/src/commands/groups.d.ts +2 -0
- package/dist/src/commands/groups.js +48 -45
- package/dist/src/commands/groups.js.map +1 -1
- package/dist/src/commands/guides.d.ts +159 -0
- package/dist/src/commands/guides.js +449 -35
- package/dist/src/commands/guides.js.map +1 -1
- package/dist/src/commands/init.d.ts +17 -0
- package/dist/src/commands/init.js +811 -201
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/integrations.d.ts +2 -0
- package/dist/src/commands/integrations.js +533 -46
- package/dist/src/commands/integrations.js.map +1 -1
- package/dist/src/commands/llm.d.ts +2 -0
- package/dist/src/commands/llm.js +4 -2
- package/dist/src/commands/llm.js.map +1 -1
- package/dist/src/commands/metadata.d.ts +2 -0
- package/dist/src/commands/metadata.js +194 -0
- package/dist/src/commands/metadata.js.map +1 -0
- package/dist/src/commands/prompts.d.ts +2 -0
- package/dist/src/commands/prompts.js +50 -52
- package/dist/src/commands/prompts.js.map +1 -1
- package/dist/src/commands/rule-sets.d.ts +3 -0
- package/dist/src/commands/rule-sets.js +388 -0
- package/dist/src/commands/rule-sets.js.map +1 -0
- package/dist/src/commands/scripts.d.ts +2 -0
- package/dist/src/commands/scripts.js +667 -0
- package/dist/src/commands/scripts.js.map +1 -0
- package/dist/src/commands/secrets.d.ts +2 -0
- package/dist/src/commands/secrets.js +108 -0
- package/dist/src/commands/secrets.js.map +1 -0
- package/dist/src/commands/skill.d.ts +2 -0
- package/dist/src/commands/skill.js +29 -0
- package/dist/src/commands/skill.js.map +1 -0
- package/dist/src/commands/sync.d.ts +175 -0
- package/dist/src/commands/sync.js +4412 -233
- package/dist/src/commands/sync.js.map +1 -1
- package/dist/src/commands/tokens.d.ts +2 -0
- package/dist/src/commands/tokens.js +122 -19
- package/dist/src/commands/tokens.js.map +1 -1
- package/dist/src/commands/users.d.ts +2 -0
- package/dist/src/commands/users.js +440 -22
- package/dist/src/commands/users.js.map +1 -1
- package/dist/src/commands/waitlist.d.ts +2 -0
- package/dist/src/commands/waitlist.js +10 -10
- package/dist/src/commands/waitlist.js.map +1 -1
- package/dist/src/commands/webhooks.d.ts +2 -0
- package/dist/src/commands/webhooks.js +391 -0
- package/dist/src/commands/webhooks.js.map +1 -0
- package/dist/src/commands/workflows.d.ts +49 -0
- package/dist/src/commands/workflows.js +1009 -123
- package/dist/src/commands/workflows.js.map +1 -1
- package/dist/src/lib/api-client.d.ts +1283 -0
- package/dist/src/lib/api-client.js +1129 -83
- package/dist/src/lib/api-client.js.map +1 -1
- package/dist/src/lib/auth-flow.d.ts +8 -0
- package/dist/src/lib/block-layout.d.ts +160 -0
- package/dist/src/lib/block-layout.js +451 -0
- package/dist/src/lib/block-layout.js.map +1 -0
- package/dist/src/lib/cli-manifest.d.ts +60 -0
- package/dist/src/lib/cli-manifest.js +70 -0
- package/dist/src/lib/cli-manifest.js.map +1 -0
- package/dist/src/lib/config.d.ts +53 -0
- package/dist/src/lib/config.js +92 -53
- package/dist/src/lib/config.js.map +1 -1
- package/dist/src/lib/confirm-prompt.d.ts +83 -0
- package/dist/src/lib/confirm-prompt.js +110 -0
- package/dist/src/lib/confirm-prompt.js.map +1 -0
- package/dist/src/lib/constants.d.ts +2 -0
- package/dist/src/lib/constants.js +3 -0
- package/dist/src/lib/constants.js.map +1 -0
- package/dist/src/lib/crash-handlers.d.ts +20 -0
- package/dist/src/lib/crash-handlers.js +49 -0
- package/dist/src/lib/crash-handlers.js.map +1 -0
- package/dist/src/lib/credentials-store.d.ts +79 -0
- package/dist/src/lib/credentials-store.js +307 -0
- package/dist/src/lib/credentials-store.js.map +1 -0
- package/dist/src/lib/csv.d.ts +48 -0
- package/dist/src/lib/csv.js +177 -0
- package/dist/src/lib/csv.js.map +1 -0
- package/dist/src/lib/db-codegen/dbFingerprint.d.ts +10 -0
- package/dist/src/lib/db-codegen/dbFingerprint.js +17 -0
- package/dist/src/lib/db-codegen/dbFingerprint.js.map +1 -0
- package/dist/src/lib/db-codegen/dbGenerator.d.ts +111 -0
- package/dist/src/lib/db-codegen/dbGenerator.js +371 -0
- package/dist/src/lib/db-codegen/dbGenerator.js.map +1 -0
- package/dist/src/lib/db-codegen/dbNaming.d.ts +45 -0
- package/dist/src/lib/db-codegen/dbNaming.js +104 -0
- package/dist/src/lib/db-codegen/dbNaming.js.map +1 -0
- package/dist/src/lib/db-codegen/dbTemplates.d.ts +97 -0
- package/dist/src/lib/db-codegen/dbTemplates.js +159 -0
- package/dist/src/lib/db-codegen/dbTemplates.js.map +1 -0
- package/dist/src/lib/db-codegen/dbTsTypes.d.ts +78 -0
- package/dist/src/lib/db-codegen/dbTsTypes.js +112 -0
- package/dist/src/lib/db-codegen/dbTsTypes.js.map +1 -0
- package/dist/src/lib/env-resolver.d.ts +62 -0
- package/dist/src/lib/env-resolver.js +121 -0
- package/dist/src/lib/env-resolver.js.map +1 -0
- package/dist/src/lib/fetch.d.ts +5 -0
- package/dist/src/lib/generated-allowlist.d.ts +28 -0
- package/dist/src/lib/generated-allowlist.js +220 -0
- package/dist/src/lib/generated-allowlist.js.map +1 -0
- package/dist/src/lib/init-config.d.ts +59 -0
- package/dist/src/lib/init-config.js +113 -0
- package/dist/src/lib/init-config.js.map +1 -0
- package/dist/src/lib/migration-nag.d.ts +49 -0
- package/dist/src/lib/migration-nag.js +163 -0
- package/dist/src/lib/migration-nag.js.map +1 -0
- package/dist/src/lib/output.d.ts +86 -0
- package/dist/src/lib/output.js +150 -8
- package/dist/src/lib/output.js.map +1 -1
- package/dist/src/lib/paginate.d.ts +33 -0
- package/dist/src/lib/paginate.js +42 -0
- package/dist/src/lib/paginate.js.map +1 -0
- package/dist/src/lib/project-config.d.ts +97 -0
- package/dist/src/lib/project-config.js +209 -0
- package/dist/src/lib/project-config.js.map +1 -0
- package/dist/src/lib/query-operators.d.ts +43 -0
- package/dist/src/lib/query-operators.js +80 -0
- package/dist/src/lib/query-operators.js.map +1 -0
- package/dist/src/lib/refresh-admin-credentials.d.ts +65 -0
- package/dist/src/lib/refresh-admin-credentials.js +103 -0
- package/dist/src/lib/refresh-admin-credentials.js.map +1 -0
- package/dist/src/lib/resolve-platform.d.ts +45 -0
- package/dist/src/lib/resolve-platform.js +43 -0
- package/dist/src/lib/resolve-platform.js.map +1 -0
- package/dist/src/lib/skill-installer.d.ts +23 -0
- package/dist/src/lib/skill-installer.js +135 -0
- package/dist/src/lib/skill-installer.js.map +1 -0
- package/dist/src/lib/snapshots.d.ts +99 -0
- package/dist/src/lib/snapshots.js +357 -0
- package/dist/src/lib/snapshots.js.map +1 -0
- package/dist/src/lib/sync-paths.d.ts +72 -0
- package/dist/src/lib/sync-paths.js +130 -0
- package/dist/src/lib/sync-paths.js.map +1 -0
- package/dist/src/lib/template.d.ts +97 -0
- package/dist/src/lib/template.js +336 -62
- package/dist/src/lib/template.js.map +1 -1
- package/dist/src/lib/token-inject.d.ts +56 -0
- package/dist/src/lib/token-inject.js +204 -0
- package/dist/src/lib/token-inject.js.map +1 -0
- package/dist/src/lib/toml-database-config.d.ts +152 -0
- package/dist/src/lib/toml-database-config.js +600 -0
- package/dist/src/lib/toml-database-config.js.map +1 -0
- package/dist/src/lib/toml-metadata-config.d.ts +139 -0
- package/dist/src/lib/toml-metadata-config.js +427 -0
- package/dist/src/lib/toml-metadata-config.js.map +1 -0
- package/dist/src/lib/toml-params-validator.d.ts +129 -0
- package/dist/src/lib/toml-params-validator.js +298 -0
- package/dist/src/lib/toml-params-validator.js.map +1 -0
- package/dist/src/lib/version-check.d.ts +10 -0
- package/dist/src/lib/version-check.js +172 -0
- package/dist/src/lib/version-check.js.map +1 -0
- package/dist/src/lib/workflow-apply.d.ts +66 -0
- package/dist/src/lib/workflow-apply.js +117 -0
- package/dist/src/lib/workflow-apply.js.map +1 -0
- package/dist/src/lib/workflow-fragments.d.ts +41 -0
- package/dist/src/lib/workflow-fragments.js +121 -0
- package/dist/src/lib/workflow-fragments.js.map +1 -0
- package/dist/src/lib/workflow-toml-validator.d.ts +95 -0
- package/dist/src/lib/workflow-toml-validator.js +323 -0
- package/dist/src/lib/workflow-toml-validator.js.map +1 -0
- package/dist/src/types/index.d.ts +520 -0
- package/dist/src/validators.d.ts +64 -0
- package/dist/src/validators.js +63 -0
- package/dist/src/validators.js.map +1 -0
- package/package.json +18 -4
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared "apply a TOML body to an existing workflow" helper.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from the `sync push` update-existing path (#1249, Fork 4) so that
|
|
5
|
+
* both `sync push` and `workflows update --from-file` push a revised body the
|
|
6
|
+
* same way. A second inlined copy would regress #807: the update is
|
|
7
|
+
* ordering-sensitive and a naive metadata-then-body sequence rejects valid
|
|
8
|
+
* sync-callable edits.
|
|
9
|
+
*
|
|
10
|
+
* Ordering invariant (#807 / codex review):
|
|
11
|
+
* 1. metadata PATCH — WITHOUT `syncCallable`. The server re-validates
|
|
12
|
+
* `syncCallable: true` against the workflow's CURRENTLY-active server
|
|
13
|
+
* steps (`loadCurrentActiveSteps`, src/admin-api.ts:4734), not the steps
|
|
14
|
+
* being pushed in the same call. So enabling sync-callable in the first
|
|
15
|
+
* PATCH would validate against the stale active steps and be rejected.
|
|
16
|
+
* 2. body write — steps to the active config (config-model) or the legacy
|
|
17
|
+
* draft slot (no active config).
|
|
18
|
+
* 3. a SECOND `syncCallable` PATCH — issued AFTER the new steps are active,
|
|
19
|
+
* so it validates against the new (compatible) steps.
|
|
20
|
+
*/
|
|
21
|
+
import type { ApiClient } from "./api-client.js";
|
|
22
|
+
/**
|
|
23
|
+
* Safely parse a JSON field that may be a string or already an object. TOML can
|
|
24
|
+
* parse inline tables as objects, so we accept both. (Same semantics as the
|
|
25
|
+
* `safeJsonParse` in sync.ts — kept here so the shared apply helper owns its
|
|
26
|
+
* own parse decision without importing back into a command module.)
|
|
27
|
+
*/
|
|
28
|
+
export declare function safeJsonParse(value: any): any;
|
|
29
|
+
export interface ApplyWorkflowBodyOptions {
|
|
30
|
+
/** Optimistic-concurrency guard for the metadata + syncCallable PATCHes. */
|
|
31
|
+
expectedModifiedAt?: string;
|
|
32
|
+
/**
|
|
33
|
+
* Active config to overwrite in place (config-model workflows). When absent,
|
|
34
|
+
* the body falls back to the legacy draft slot — non-live for config-model
|
|
35
|
+
* workflows, which the server rejects with 409 WORKFLOW_CONFIG_MODEL.
|
|
36
|
+
*/
|
|
37
|
+
activeConfigId?: string;
|
|
38
|
+
/** When true, skip the optimistic-concurrency check (mirrors `--force`). */
|
|
39
|
+
force?: boolean;
|
|
40
|
+
}
|
|
41
|
+
export interface ApplyWorkflowBodyResult {
|
|
42
|
+
/** The freshest workflow `modifiedAt` after all PATCHes (for sync state). */
|
|
43
|
+
latestModifiedAt?: string;
|
|
44
|
+
/** Which slot the steps landed in: "active config" or "draft (legacy)". */
|
|
45
|
+
updateSlotLabel: string;
|
|
46
|
+
/** A fresh `getWorkflow` result so callers can read back configs/state. */
|
|
47
|
+
fullWorkflow: any;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Apply a parsed workflow body (metadata + steps) to an existing workflow,
|
|
51
|
+
* preserving the key/id. `workflow` is the TOML's `[workflow]` table (already
|
|
52
|
+
* merged with any explicit CLI flag overrides by the caller); `steps` is the
|
|
53
|
+
* TOML's `steps` array. The function performs the three-step ordered update
|
|
54
|
+
* and returns the latest `modifiedAt`, the slot label, and a fresh workflow.
|
|
55
|
+
*/
|
|
56
|
+
export declare function applyWorkflowBody(client: ApiClient, appId: string, workflowId: string, workflow: any, steps: any[], opts?: ApplyWorkflowBodyOptions): Promise<ApplyWorkflowBodyResult>;
|
|
57
|
+
/**
|
|
58
|
+
* Fork 2 (#1249): a TOML pushed at a specific `<id>` may also declare a `key` /
|
|
59
|
+
* `workflowKey`. If it differs from the target's actual key, that's almost
|
|
60
|
+
* certainly the wrong file — error rather than silently ignoring it. An absent
|
|
61
|
+
* TOML key is advisory and allowed (the operation targets `<id>`).
|
|
62
|
+
*
|
|
63
|
+
* Returns an error message string when there is a mismatch, otherwise null.
|
|
64
|
+
* Pure function for unit testing.
|
|
65
|
+
*/
|
|
66
|
+
export declare function workflowKeyMismatchError(tomlWorkflow: any, existingWorkflowKey: string | undefined): string | null;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared "apply a TOML body to an existing workflow" helper.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from the `sync push` update-existing path (#1249, Fork 4) so that
|
|
5
|
+
* both `sync push` and `workflows update --from-file` push a revised body the
|
|
6
|
+
* same way. A second inlined copy would regress #807: the update is
|
|
7
|
+
* ordering-sensitive and a naive metadata-then-body sequence rejects valid
|
|
8
|
+
* sync-callable edits.
|
|
9
|
+
*
|
|
10
|
+
* Ordering invariant (#807 / codex review):
|
|
11
|
+
* 1. metadata PATCH — WITHOUT `syncCallable`. The server re-validates
|
|
12
|
+
* `syncCallable: true` against the workflow's CURRENTLY-active server
|
|
13
|
+
* steps (`loadCurrentActiveSteps`, src/admin-api.ts:4734), not the steps
|
|
14
|
+
* being pushed in the same call. So enabling sync-callable in the first
|
|
15
|
+
* PATCH would validate against the stale active steps and be rejected.
|
|
16
|
+
* 2. body write — steps to the active config (config-model) or the legacy
|
|
17
|
+
* draft slot (no active config).
|
|
18
|
+
* 3. a SECOND `syncCallable` PATCH — issued AFTER the new steps are active,
|
|
19
|
+
* so it validates against the new (compatible) steps.
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* Safely parse a JSON field that may be a string or already an object. TOML can
|
|
23
|
+
* parse inline tables as objects, so we accept both. (Same semantics as the
|
|
24
|
+
* `safeJsonParse` in sync.ts — kept here so the shared apply helper owns its
|
|
25
|
+
* own parse decision without importing back into a command module.)
|
|
26
|
+
*/
|
|
27
|
+
export function safeJsonParse(value) {
|
|
28
|
+
if (value === undefined || value === null)
|
|
29
|
+
return undefined;
|
|
30
|
+
if (typeof value === "string")
|
|
31
|
+
return JSON.parse(value);
|
|
32
|
+
return value; // Already an object (TOML parsed it as a table)
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Apply a parsed workflow body (metadata + steps) to an existing workflow,
|
|
36
|
+
* preserving the key/id. `workflow` is the TOML's `[workflow]` table (already
|
|
37
|
+
* merged with any explicit CLI flag overrides by the caller); `steps` is the
|
|
38
|
+
* TOML's `steps` array. The function performs the three-step ordered update
|
|
39
|
+
* and returns the latest `modifiedAt`, the slot label, and a fresh workflow.
|
|
40
|
+
*/
|
|
41
|
+
export async function applyWorkflowBody(client, appId, workflowId, workflow, steps, opts = {}) {
|
|
42
|
+
const { expectedModifiedAt, activeConfigId, force } = opts;
|
|
43
|
+
// 1. Metadata PATCH — note: NO `syncCallable` here (deferred to step 3).
|
|
44
|
+
// `requiresClientApply` has no step-dependent validation, so it stays.
|
|
45
|
+
const updated = await client.updateWorkflow(appId, workflowId, {
|
|
46
|
+
name: workflow.name,
|
|
47
|
+
description: workflow.description,
|
|
48
|
+
status: workflow.status,
|
|
49
|
+
accessRule: workflow.accessRule !== undefined ? workflow.accessRule || null : undefined,
|
|
50
|
+
// #1081 — workflow principal mode. Absent key → undefined (server leaves
|
|
51
|
+
// it untouched); explicit value/empty is forwarded so it round-trips.
|
|
52
|
+
runAs: workflow.runAs !== undefined ? workflow.runAs || null : undefined,
|
|
53
|
+
// #1233 — opt-in capability grants. Absent key → undefined (server's
|
|
54
|
+
// hasOwnProperty guard leaves it untouched); an explicit array is
|
|
55
|
+
// forwarded so a pull → push round-trips it. Mirrors `runAs` above so
|
|
56
|
+
// both `sync push` and `workflows update --from-file` apply it the same.
|
|
57
|
+
capabilities: workflow.capabilities !== undefined ? workflow.capabilities : undefined,
|
|
58
|
+
perUserMaxRunning: workflow.perUserMaxRunning,
|
|
59
|
+
perUserMaxQueued: workflow.perUserMaxQueued,
|
|
60
|
+
dequeueOrder: workflow.dequeueOrder,
|
|
61
|
+
requiresClientApply: workflow.requiresClientApply,
|
|
62
|
+
inputSchema: workflow.inputSchema ? safeJsonParse(workflow.inputSchema) : null,
|
|
63
|
+
outputSchema: workflow.outputSchema ? safeJsonParse(workflow.outputSchema) : null,
|
|
64
|
+
// #1304 (P-C): declared-access manifest. Absent key → undefined (server's
|
|
65
|
+
// hasOwnProperty guard leaves it untouched); a parsed value/null is
|
|
66
|
+
// forwarded so a pull → push round-trips it (null clears). `sync push`
|
|
67
|
+
// always attaches this (null when the TOML drops `[metadata]`); the
|
|
68
|
+
// `workflows update --from-file` caller may leave it undefined.
|
|
69
|
+
metadataManifest: workflow.metadataManifest !== undefined ? workflow.metadataManifest : undefined,
|
|
70
|
+
}, expectedModifiedAt);
|
|
71
|
+
// The first PATCH bumps `modifiedAt`; the syncCallable PATCH (if any) bumps
|
|
72
|
+
// it again. The config/step update does NOT touch the workflow definition's
|
|
73
|
+
// `modifiedAt`.
|
|
74
|
+
let latestModifiedAt = updated?.workflow?.modifiedAt;
|
|
75
|
+
// 2. Body write — active config (config-model) or legacy draft slot.
|
|
76
|
+
let updateSlotLabel = "active config";
|
|
77
|
+
if (activeConfigId) {
|
|
78
|
+
await client.updateWorkflowConfig(appId, workflowId, activeConfigId, { steps });
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
await client.updateWorkflowDraft(appId, workflowId, {
|
|
82
|
+
steps,
|
|
83
|
+
inputSchema: workflow.inputSchema ? safeJsonParse(workflow.inputSchema) : null,
|
|
84
|
+
});
|
|
85
|
+
updateSlotLabel = "draft (legacy)";
|
|
86
|
+
}
|
|
87
|
+
// 3. Deferred `syncCallable` PATCH — now that the new steps are active (#807).
|
|
88
|
+
// Only sent when the TOML actually carries the key; an absent key stays
|
|
89
|
+
// `undefined` and the call is skipped, preserving no-clobber discipline.
|
|
90
|
+
if (workflow.syncCallable !== undefined) {
|
|
91
|
+
const syncCallableUpdated = await client.updateWorkflow(appId, workflowId, { syncCallable: workflow.syncCallable }, force ? undefined : latestModifiedAt);
|
|
92
|
+
if (syncCallableUpdated?.workflow?.modifiedAt) {
|
|
93
|
+
latestModifiedAt = syncCallableUpdated.workflow.modifiedAt;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
const fullWorkflow = await client.getWorkflow(appId, workflowId);
|
|
97
|
+
return { latestModifiedAt, updateSlotLabel, fullWorkflow };
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Fork 2 (#1249): a TOML pushed at a specific `<id>` may also declare a `key` /
|
|
101
|
+
* `workflowKey`. If it differs from the target's actual key, that's almost
|
|
102
|
+
* certainly the wrong file — error rather than silently ignoring it. An absent
|
|
103
|
+
* TOML key is advisory and allowed (the operation targets `<id>`).
|
|
104
|
+
*
|
|
105
|
+
* Returns an error message string when there is a mismatch, otherwise null.
|
|
106
|
+
* Pure function for unit testing.
|
|
107
|
+
*/
|
|
108
|
+
export function workflowKeyMismatchError(tomlWorkflow, existingWorkflowKey) {
|
|
109
|
+
const tomlKey = tomlWorkflow?.key ?? tomlWorkflow?.workflowKey;
|
|
110
|
+
if (tomlKey && existingWorkflowKey && tomlKey !== existingWorkflowKey) {
|
|
111
|
+
return (`TOML workflow key "${tomlKey}" does not match the target workflow's key ` +
|
|
112
|
+
`"${existingWorkflowKey}". This usually means you pointed --from-file at the ` +
|
|
113
|
+
`wrong workflow id. Remove the key from the TOML or target the matching id.`);
|
|
114
|
+
}
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=workflow-apply.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow-apply.js","sourceRoot":"","sources":["../../../src/lib/workflow-apply.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI-only workflow fragment expansion.
|
|
3
|
+
*
|
|
4
|
+
* Workflows may declare `include = ["fragment-name", ...]` at the top of
|
|
5
|
+
* their TOML. The CLI expands those references into a fully-flattened
|
|
6
|
+
* `[[steps]]` list before push — the server never sees fragments and
|
|
7
|
+
* stores only the canonical, expanded JSON.
|
|
8
|
+
*
|
|
9
|
+
* Pinned decisions (#744):
|
|
10
|
+
* - CLI-only. No server-side WorkflowFragment model.
|
|
11
|
+
* - Fragments live at <workflowDir>/../workflow-fragments/<name>.toml.
|
|
12
|
+
* - Fragment files are `[[steps]]` lists only — no `[workflow]` block.
|
|
13
|
+
* - No recursive includes in v1 (fragments cannot themselves `include`).
|
|
14
|
+
* - Unique step ids validated post-expansion; collisions name both
|
|
15
|
+
* locations in the error message.
|
|
16
|
+
*
|
|
17
|
+
* Wiring: `parseTomlFile()` in `cli/src/commands/sync.ts` calls
|
|
18
|
+
* `expandWorkflowTomlData()` after parsing. All 24 push parse sites in
|
|
19
|
+
* sync.ts route through that single seam, so the expansion is uniform.
|
|
20
|
+
* The standalone `expandWorkflow(filePath)` helper backs the
|
|
21
|
+
* `primitive workflows expand` subcommand for debugging.
|
|
22
|
+
*/
|
|
23
|
+
export interface ExpandedWorkflow {
|
|
24
|
+
workflow?: Record<string, any>;
|
|
25
|
+
steps: Array<Record<string, any>>;
|
|
26
|
+
include?: undefined;
|
|
27
|
+
[key: string]: any;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Read and expand a workflow TOML file from disk. Returns the parsed
|
|
31
|
+
* TOML with all `include` fragments spliced in. If the file has no
|
|
32
|
+
* `include` key, the parsed TOML is returned unchanged.
|
|
33
|
+
*/
|
|
34
|
+
export declare function expandWorkflow(workflowPath: string): ExpandedWorkflow;
|
|
35
|
+
/**
|
|
36
|
+
* Expand the `include` key of an already-parsed TOML object. Resolves
|
|
37
|
+
* fragment files relative to `<workflowPath>/../../workflow-fragments/`.
|
|
38
|
+
* Use this when the TOML has already been parsed elsewhere (e.g. inside
|
|
39
|
+
* `parseTomlFile()`).
|
|
40
|
+
*/
|
|
41
|
+
export declare function expandWorkflowTomlData(parsed: Record<string, any>, workflowPath: string): ExpandedWorkflow;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI-only workflow fragment expansion.
|
|
3
|
+
*
|
|
4
|
+
* Workflows may declare `include = ["fragment-name", ...]` at the top of
|
|
5
|
+
* their TOML. The CLI expands those references into a fully-flattened
|
|
6
|
+
* `[[steps]]` list before push — the server never sees fragments and
|
|
7
|
+
* stores only the canonical, expanded JSON.
|
|
8
|
+
*
|
|
9
|
+
* Pinned decisions (#744):
|
|
10
|
+
* - CLI-only. No server-side WorkflowFragment model.
|
|
11
|
+
* - Fragments live at <workflowDir>/../workflow-fragments/<name>.toml.
|
|
12
|
+
* - Fragment files are `[[steps]]` lists only — no `[workflow]` block.
|
|
13
|
+
* - No recursive includes in v1 (fragments cannot themselves `include`).
|
|
14
|
+
* - Unique step ids validated post-expansion; collisions name both
|
|
15
|
+
* locations in the error message.
|
|
16
|
+
*
|
|
17
|
+
* Wiring: `parseTomlFile()` in `cli/src/commands/sync.ts` calls
|
|
18
|
+
* `expandWorkflowTomlData()` after parsing. All 24 push parse sites in
|
|
19
|
+
* sync.ts route through that single seam, so the expansion is uniform.
|
|
20
|
+
* The standalone `expandWorkflow(filePath)` helper backs the
|
|
21
|
+
* `primitive workflows expand` subcommand for debugging.
|
|
22
|
+
*/
|
|
23
|
+
import { existsSync, readFileSync } from "fs";
|
|
24
|
+
import { dirname, join, basename } from "path";
|
|
25
|
+
import * as TOML from "@iarna/toml";
|
|
26
|
+
/**
|
|
27
|
+
* Read and expand a workflow TOML file from disk. Returns the parsed
|
|
28
|
+
* TOML with all `include` fragments spliced in. If the file has no
|
|
29
|
+
* `include` key, the parsed TOML is returned unchanged.
|
|
30
|
+
*/
|
|
31
|
+
export function expandWorkflow(workflowPath) {
|
|
32
|
+
const content = readFileSync(workflowPath, "utf-8");
|
|
33
|
+
const parsed = TOML.parse(content);
|
|
34
|
+
return expandWorkflowTomlData(parsed, workflowPath);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Expand the `include` key of an already-parsed TOML object. Resolves
|
|
38
|
+
* fragment files relative to `<workflowPath>/../../workflow-fragments/`.
|
|
39
|
+
* Use this when the TOML has already been parsed elsewhere (e.g. inside
|
|
40
|
+
* `parseTomlFile()`).
|
|
41
|
+
*/
|
|
42
|
+
export function expandWorkflowTomlData(parsed, workflowPath) {
|
|
43
|
+
// Fast path: nothing to expand. Preserve the original shape exactly —
|
|
44
|
+
// including the absence of a `steps` field, since not every TOML file
|
|
45
|
+
// routed through `parseTomlFile()` represents a workflow.
|
|
46
|
+
if (!("include" in parsed)) {
|
|
47
|
+
return parsed;
|
|
48
|
+
}
|
|
49
|
+
const includeList = parsed.include;
|
|
50
|
+
if (!Array.isArray(includeList)) {
|
|
51
|
+
throw new Error(`Workflow '${workflowPath}' has an 'include' key but it is not an array. Use \`include = ["fragment-name"]\`.`);
|
|
52
|
+
}
|
|
53
|
+
// Resolve fragments directory: sibling of workflow's parent directory.
|
|
54
|
+
// E.g. workflow at config/workflows/foo.toml
|
|
55
|
+
// fragments at config/workflow-fragments/<name>.toml
|
|
56
|
+
const fragmentsDir = join(dirname(workflowPath), "..", "workflow-fragments");
|
|
57
|
+
const workflowOwnSteps = Array.isArray(parsed.steps)
|
|
58
|
+
? parsed.steps
|
|
59
|
+
: [];
|
|
60
|
+
const allOriginated = [];
|
|
61
|
+
for (const fragmentName of includeList) {
|
|
62
|
+
if (typeof fragmentName !== "string" || fragmentName.length === 0) {
|
|
63
|
+
throw new Error(`Workflow '${workflowPath}' has an invalid include entry: ${JSON.stringify(fragmentName)}. Each entry must be a fragment name string.`);
|
|
64
|
+
}
|
|
65
|
+
const fragmentPath = join(fragmentsDir, `${fragmentName}.toml`);
|
|
66
|
+
const fragment = readFragmentSafely(fragmentPath, workflowPath, fragmentName);
|
|
67
|
+
for (const step of fragment.steps) {
|
|
68
|
+
allOriginated.push({ step, origin: `fragment '${fragmentName}'` });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
for (const step of workflowOwnSteps) {
|
|
72
|
+
allOriginated.push({ step, origin: `workflow '${basename(workflowPath)}'` });
|
|
73
|
+
}
|
|
74
|
+
assertUniqueStepIds(allOriginated, workflowPath);
|
|
75
|
+
// Build the expanded object: drop `include`, replace `steps` with the
|
|
76
|
+
// concatenated list. Preserve all other top-level keys (workflow,
|
|
77
|
+
// triggers, configs, etc.) exactly as parsed.
|
|
78
|
+
const result = { ...parsed };
|
|
79
|
+
delete result.include;
|
|
80
|
+
result.steps = allOriginated.map((o) => o.step);
|
|
81
|
+
return result;
|
|
82
|
+
}
|
|
83
|
+
function readFragmentSafely(fragmentPath, workflowPath, fragmentName) {
|
|
84
|
+
if (!existsSync(fragmentPath)) {
|
|
85
|
+
throw new Error(`Workflow '${workflowPath}' includes fragment '${fragmentName}' but the file does not exist at '${fragmentPath}'.`);
|
|
86
|
+
}
|
|
87
|
+
let parsed;
|
|
88
|
+
try {
|
|
89
|
+
const content = readFileSync(fragmentPath, "utf-8");
|
|
90
|
+
parsed = TOML.parse(content);
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
throw new Error(`Failed to parse fragment '${fragmentName}' at '${fragmentPath}': ${err?.message ?? err}`);
|
|
94
|
+
}
|
|
95
|
+
if ("include" in parsed) {
|
|
96
|
+
throw new Error(`Fragment '${fragmentName}' at '${fragmentPath}' contains its own 'include' key — recursive includes are not supported in v1. Inline the steps directly into this fragment.`);
|
|
97
|
+
}
|
|
98
|
+
if ("workflow" in parsed) {
|
|
99
|
+
throw new Error(`Fragment '${fragmentName}' at '${fragmentPath}' contains a [workflow] block. Fragment files must be [[steps]] lists only.`);
|
|
100
|
+
}
|
|
101
|
+
const steps = Array.isArray(parsed.steps)
|
|
102
|
+
? parsed.steps
|
|
103
|
+
: [];
|
|
104
|
+
return { steps };
|
|
105
|
+
}
|
|
106
|
+
function assertUniqueStepIds(originated, workflowPath) {
|
|
107
|
+
const seen = new Map();
|
|
108
|
+
for (const { step, origin } of originated) {
|
|
109
|
+
const id = step?.id;
|
|
110
|
+
if (id === undefined || id === null)
|
|
111
|
+
continue; // let downstream code complain about missing ids
|
|
112
|
+
if (typeof id !== "string")
|
|
113
|
+
continue;
|
|
114
|
+
if (seen.has(id)) {
|
|
115
|
+
const firstOrigin = seen.get(id);
|
|
116
|
+
throw new Error(`Step id '${id}' appears twice when expanding workflow '${workflowPath}': first in ${firstOrigin}, then in ${origin}.`);
|
|
117
|
+
}
|
|
118
|
+
seen.set(id, origin);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=workflow-fragments.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow-fragments.js","sourceRoot":"","sources":["../../../src/lib/workflow-fragments.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Push-time validator for workflow TOML files (issue #685, #998).
|
|
3
|
+
*
|
|
4
|
+
* Detects the common footgun where a user writes `[steps.<id>.<field>]` under
|
|
5
|
+
* an open `[[steps]]` array. TOML parses that as a sub-table on the
|
|
6
|
+
* most-recent step keyed by the step's id (e.g. `steps[0]["refresh-each"]`),
|
|
7
|
+
* not as the intended `steps[0].request`. The result is a step with an
|
|
8
|
+
* unrecognized top-level field — the runtime silently ignores it, and the
|
|
9
|
+
* step then runs with an empty `request` block.
|
|
10
|
+
*
|
|
11
|
+
* The validator walks every step in `tomlData.steps[]`. For a step whose
|
|
12
|
+
* `kind` the CLI KNOWS about, it reports any top-level field not in the
|
|
13
|
+
* generated allowlist. For an UNKNOWN kind (an old CLI run against a newer
|
|
14
|
+
* server that registered a step kind the CLI doesn't have), it SKIPS the
|
|
15
|
+
* per-field allowlist check — but still runs the kind-agnostic
|
|
16
|
+
* misnested-header footgun detector. This is the #998 fix: the recurring
|
|
17
|
+
* "newer server kind → false-reject of valid fields" failure (#685/#802/#971
|
|
18
|
+
* were all false rejections) becomes "unvalidated, not rejected."
|
|
19
|
+
*
|
|
20
|
+
* ── Source of truth (#998) ───────────────────────────────────────────────
|
|
21
|
+
* The allowlist field union and the known-kind set are GENERATED from the
|
|
22
|
+
* server step runners (`src/workflows/steps/*.ts`) and the universal
|
|
23
|
+
* `BaseStepDefinition` shape into the committed
|
|
24
|
+
* `cli/src/lib/generated-allowlist.ts` artifact (the CLI does NOT import
|
|
25
|
+
* server `src/` at runtime — only the build-time generator does). The union is
|
|
26
|
+
* derived from what each runner READS, not what the `*StepDefinition` TYPE
|
|
27
|
+
* declares — types over-declare and would weaken the footgun detector. A CI
|
|
28
|
+
* freshness guard (`gen-allowlist.mjs --check`, also asserted by the #985
|
|
29
|
+
* drift-guard test) keeps the committed artifact in sync with the runners, so
|
|
30
|
+
* the hand-edit drift class (#685/#802/#971) cannot recur for the same-version
|
|
31
|
+
* case. Regenerate with `node cli/scripts/gen-allowlist.mjs`.
|
|
32
|
+
*/
|
|
33
|
+
/**
|
|
34
|
+
* Read-only view of `ALLOWLISTED_FIELDS` for the drift-guard test (#985).
|
|
35
|
+
*
|
|
36
|
+
* Exported so `cli/tests/unit/workflow-toml-validator-drift-guard.test.ts` can
|
|
37
|
+
* assert this set stays a superset of every top-level field the step runners
|
|
38
|
+
* in `src/workflows/steps/*.ts` actually read — checking the *live* set the
|
|
39
|
+
* validator enforces rather than a copy that could itself drift. Not part of
|
|
40
|
+
* the public validator API; consumed only by the guard.
|
|
41
|
+
*/
|
|
42
|
+
export declare const ALLOWLISTED_FIELDS_FOR_DRIFT_GUARD: ReadonlySet<string>;
|
|
43
|
+
/**
|
|
44
|
+
* Read-only view of `KNOWN_KINDS` for the drift-guard test (#998). Lets the
|
|
45
|
+
* guard assert the generated known-kind set matches a fresh scan.
|
|
46
|
+
*/
|
|
47
|
+
export declare const KNOWN_KINDS_FOR_DRIFT_GUARD: ReadonlySet<string>;
|
|
48
|
+
/**
|
|
49
|
+
* A single offending field on a step.
|
|
50
|
+
*
|
|
51
|
+
* `stepIndex` is the array index in `steps[]`.
|
|
52
|
+
* `stepId` is the value of the step's `id` field, if any (string).
|
|
53
|
+
* `field` is the unknown top-level key; uses sentinel names for shape
|
|
54
|
+
* errors:
|
|
55
|
+
* - `__steps_shape__`: `steps` is not an array (table or scalar).
|
|
56
|
+
* - `__step_shape__`: a step is not an object.
|
|
57
|
+
* `hint` is a short, user-facing explanation (used by
|
|
58
|
+
* `formatWorkflowTomlErrors` to render the multi-line diagnostic).
|
|
59
|
+
* `parentStepId` is set when this error came from a `collect` step's
|
|
60
|
+
* nested inner `step` definition (codex review, 2026-05-15): the outer
|
|
61
|
+
* `stepIndex` points at the array index, `parentStepId` carries the
|
|
62
|
+
* outer step's id (if any), and `stepId` carries the inner step's id.
|
|
63
|
+
* It's `null` for top-level errors (the renderer treats both `null` and
|
|
64
|
+
* `undefined` as top-level — see `formatWorkflowTomlErrors`).
|
|
65
|
+
*/
|
|
66
|
+
export type WorkflowTomlError = {
|
|
67
|
+
stepIndex: number;
|
|
68
|
+
stepId: string | null;
|
|
69
|
+
field: string;
|
|
70
|
+
hint: string;
|
|
71
|
+
parentStepId?: string | null;
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Walk a parsed workflow TOML document and return all unknown-field errors.
|
|
75
|
+
*
|
|
76
|
+
* @param tomlData The output of `TOML.parse()` for a workflow TOML file.
|
|
77
|
+
* @returns An array of error objects, one per offending field. Empty if no
|
|
78
|
+
* errors were found.
|
|
79
|
+
*/
|
|
80
|
+
export declare function validateWorkflowToml(tomlData: any): WorkflowTomlError[];
|
|
81
|
+
/**
|
|
82
|
+
* Render a multi-line diagnostic for a list of errors. Designed for the
|
|
83
|
+
* shape called out in the issue:
|
|
84
|
+
*
|
|
85
|
+
* Error in workflows/refresh.toml:
|
|
86
|
+
* steps[0] (id="refresh-each") has unknown field "refresh-each".
|
|
87
|
+
* This is usually caused by writing [steps.refresh-each.request] instead of [steps.request].
|
|
88
|
+
* TOML can't address an array element by id — use [steps.request] (refers to the most recent step).
|
|
89
|
+
*
|
|
90
|
+
* @param filePath The TOML file path (or display name) to include in the
|
|
91
|
+
* header.
|
|
92
|
+
* @param errors The error list from `validateWorkflowToml`.
|
|
93
|
+
* @returns A multi-line string ready to print to stderr.
|
|
94
|
+
*/
|
|
95
|
+
export declare function formatWorkflowTomlErrors(filePath: string, errors: WorkflowTomlError[]): string;
|