feat: authorize workflow contract authoring
This commit is contained in:
@@ -2,6 +2,8 @@ import { Schema } from "effect";
|
||||
import type {
|
||||
NodeSpecCapabilitySummary,
|
||||
StepInputBinding,
|
||||
AuthoringContractInventoryPayload,
|
||||
AuthoringPathOptionPayload,
|
||||
WorkflowOperationName,
|
||||
WrapperArtifactCapabilitySummary,
|
||||
} from "./generated/workflow-contract.js";
|
||||
@@ -18,6 +20,8 @@ import {
|
||||
WorkflowDraftWorkspacesListResultSchema,
|
||||
WorkflowDraftWorkspacesGetPayloadSchema,
|
||||
WorkflowDraftWorkspacesGetResultSchema,
|
||||
WorkflowDraftWorkspacesInspectAuthoringContractPayloadSchema,
|
||||
WorkflowDraftWorkspacesInspectAuthoringContractResultSchema,
|
||||
WorkflowDraftWorkspacesCreateEmptyPayloadSchema,
|
||||
WorkflowDraftWorkspacesCreateEmptyResultSchema,
|
||||
WorkflowDraftWorkspacesCreateFromCapabilityPayloadSchema,
|
||||
@@ -28,10 +32,16 @@ import {
|
||||
WorkflowDraftWorkspacesUpdateCapabilityStepResultSchema,
|
||||
WorkflowDraftWorkspacesSetRoutePayloadSchema,
|
||||
WorkflowDraftWorkspacesSetRouteResultSchema,
|
||||
WorkflowDraftWorkspacesSetContractPayloadSchema,
|
||||
WorkflowDraftWorkspacesSetContractResultSchema,
|
||||
WorkflowDraftWorkspacesSetStartPayloadSchema,
|
||||
WorkflowDraftWorkspacesSetStartResultSchema,
|
||||
WorkflowDraftWorkspacesSetStepInputBindingsPayloadSchema,
|
||||
WorkflowDraftWorkspacesSetStepInputBindingsResultSchema,
|
||||
WorkflowDraftWorkspacesSetStepOutputBindingsPayloadSchema,
|
||||
WorkflowDraftWorkspacesSetStepOutputBindingsResultSchema,
|
||||
WorkflowDraftWorkspacesSetWorkflowOutputBindingsPayloadSchema,
|
||||
WorkflowDraftWorkspacesSetWorkflowOutputBindingsResultSchema,
|
||||
WorkflowDraftWorkspacesValidatePayloadSchema,
|
||||
WorkflowDraftWorkspacesValidateResultSchema,
|
||||
WorkflowArtifactsListPayloadSchema,
|
||||
@@ -141,6 +151,27 @@ export type DraftWorkspaceInterpreted = {
|
||||
readonly draft: Readonly<Record<string, unknown>> | null;
|
||||
};
|
||||
|
||||
export type AuthoringContractInterpreted = {
|
||||
readonly workspaceId: string;
|
||||
readonly revision: number;
|
||||
readonly selectedStepId: string | null;
|
||||
readonly readableSources: ReadonlyArray<ReturnType<typeof interpretAuthoringPathOption>>;
|
||||
readonly stepInputTargets: ReadonlyArray<ReturnType<typeof interpretAuthoringPathOption>>;
|
||||
readonly stepOutputSources: ReadonlyArray<ReturnType<typeof interpretAuthoringPathOption>>;
|
||||
readonly stateTargets: ReadonlyArray<ReturnType<typeof interpretAuthoringPathOption>>;
|
||||
readonly workflowOutputTargets: ReadonlyArray<ReturnType<typeof interpretAuthoringPathOption>>;
|
||||
readonly entrySteps: ReadonlyArray<{
|
||||
readonly stepId: string;
|
||||
readonly label: string;
|
||||
readonly description?: string;
|
||||
readonly inputTargets?: ReadonlyArray<ReturnType<typeof interpretAuthoringPathOption>>;
|
||||
readonly outputSources?: ReadonlyArray<ReturnType<typeof interpretAuthoringPathOption>>;
|
||||
readonly outcomes?: ReadonlyArray<string>;
|
||||
}>;
|
||||
readonly workflowOutcomes: ReadonlyArray<string>;
|
||||
readonly warnings: ReadonlyArray<string>;
|
||||
};
|
||||
|
||||
const interpretNextActions = (nextActions: {
|
||||
readonly can_continue: boolean;
|
||||
readonly can_save_now: boolean | null;
|
||||
@@ -301,6 +332,49 @@ const interpretDraftWorkspace = (decoded: {
|
||||
draft: decoded.draft ?? null,
|
||||
});
|
||||
|
||||
const interpretAuthoringPathOption = (option: AuthoringPathOptionPayload) => ({
|
||||
path: option.path,
|
||||
label: option.label,
|
||||
origin: option.origin,
|
||||
schema: option.schema,
|
||||
required: option.required,
|
||||
availability: option.availability,
|
||||
uses: option.uses,
|
||||
...(option.description === undefined
|
||||
? {}
|
||||
: { description: option.description }),
|
||||
...(option.reason === undefined ? {} : { reason: option.reason }),
|
||||
});
|
||||
|
||||
const interpretAuthoringContract = (
|
||||
decoded: AuthoringContractInventoryPayload,
|
||||
): AuthoringContractInterpreted => ({
|
||||
workspaceId: decoded.workspace_id,
|
||||
revision: decoded.revision,
|
||||
selectedStepId: decoded.selected_step_id,
|
||||
readableSources: decoded.readable_sources.map(interpretAuthoringPathOption),
|
||||
stepInputTargets: decoded.step_input_targets.map(interpretAuthoringPathOption),
|
||||
stepOutputSources: decoded.step_output_sources.map(interpretAuthoringPathOption),
|
||||
stateTargets: decoded.state_targets.map(interpretAuthoringPathOption),
|
||||
workflowOutputTargets: decoded.workflow_output_targets.map(
|
||||
interpretAuthoringPathOption,
|
||||
),
|
||||
entrySteps: decoded.entry_steps.map((step) => ({
|
||||
stepId: step.step_id,
|
||||
label: step.label,
|
||||
...(step.description === undefined ? {} : { description: step.description }),
|
||||
...(step.input_targets === undefined
|
||||
? {}
|
||||
: { inputTargets: step.input_targets.map(interpretAuthoringPathOption) }),
|
||||
...(step.output_sources === undefined
|
||||
? {}
|
||||
: { outputSources: step.output_sources.map(interpretAuthoringPathOption) }),
|
||||
...(step.outcomes === undefined ? {} : { outcomes: step.outcomes }),
|
||||
})),
|
||||
workflowOutcomes: decoded.workflow_outcomes,
|
||||
warnings: decoded.warnings,
|
||||
});
|
||||
|
||||
/** Adapts a snake_case run detail from the server into camelCase for the browser. */
|
||||
const interpretRunDetail = (decoded: {
|
||||
readonly run_id: string | null;
|
||||
@@ -533,6 +607,33 @@ const operationEntries = defineOperationEntries([
|
||||
return interpretDraftWorkspace(decoded);
|
||||
},
|
||||
},
|
||||
{
|
||||
method: "workflow.draft_workspaces.inspect_authoring_contract",
|
||||
label: "Inspect draft authoring contract",
|
||||
explanation: "Inspect schema-derived sources and targets for a draft workspace",
|
||||
idempotency: "read",
|
||||
equivalentCli: (params) => {
|
||||
const p = Schema.decodeUnknownSync(
|
||||
WorkflowDraftWorkspacesInspectAuthoringContractPayloadSchema,
|
||||
)(params, { onExcessProperty: "error" });
|
||||
return nonEquivalentCli(
|
||||
`uv run wf draft inspect ${shellArg(p.workspace_id)} --include-draft`,
|
||||
[
|
||||
"revision",
|
||||
...(p.selected_step_id == null ? [] : ["selected_step_id"]),
|
||||
],
|
||||
);
|
||||
},
|
||||
interpret: (result) => {
|
||||
const decoded = Schema.decodeUnknownSync(
|
||||
WorkflowDraftWorkspacesInspectAuthoringContractResultSchema,
|
||||
)(result);
|
||||
if (isRecord(decoded) && "readable_sources" in decoded) {
|
||||
return interpretAuthoringContract(decoded as AuthoringContractInventoryPayload);
|
||||
}
|
||||
return interpretDraftWorkspace(decoded);
|
||||
},
|
||||
},
|
||||
{
|
||||
method: "workflow.draft_workspaces.create_empty",
|
||||
label: "Create empty draft workspace",
|
||||
@@ -722,6 +823,55 @@ const operationEntries = defineOperationEntries([
|
||||
return interpretDraftWorkspace(decoded);
|
||||
},
|
||||
},
|
||||
{
|
||||
method: "workflow.draft_workspaces.set_contract",
|
||||
label: "Set draft contract",
|
||||
explanation: "Replace selected workflow input, state, output, or outcome contract fields",
|
||||
idempotency: "write",
|
||||
equivalentCli: (params) => {
|
||||
const p = Schema.decodeUnknownSync(
|
||||
WorkflowDraftWorkspacesSetContractPayloadSchema,
|
||||
)(params, { onExcessProperty: "error" });
|
||||
const parts = [
|
||||
"uv run wf draft set-contract",
|
||||
shellArg(p.workspace_id),
|
||||
"--revision",
|
||||
String(p.revision),
|
||||
];
|
||||
const unavailable: string[] = [];
|
||||
if (p.input_schema != null) unavailable.push("input_schema (use --input-schema-file)");
|
||||
if (p.state_schema != null) unavailable.push("state_schema (use --state-schema-file)");
|
||||
if (p.output_schema != null) unavailable.push("output_schema (use --output-schema-file)");
|
||||
for (const outcome of p.outcomes ?? []) {
|
||||
parts.push("--outcome", shellArg(outcome));
|
||||
}
|
||||
return nonEquivalentCli(parts.join(" "), unavailable);
|
||||
},
|
||||
interpret: (result) => {
|
||||
const decoded = Schema.decodeUnknownSync(
|
||||
WorkflowDraftWorkspacesSetContractResultSchema,
|
||||
)(result);
|
||||
return interpretDraftWorkspace(decoded);
|
||||
},
|
||||
},
|
||||
{
|
||||
method: "workflow.draft_workspaces.set_start",
|
||||
label: "Set draft start",
|
||||
explanation: "Set the entry step for a persisted workflow draft",
|
||||
idempotency: "write",
|
||||
equivalentCli: (params) => {
|
||||
const p = Schema.decodeUnknownSync(
|
||||
WorkflowDraftWorkspacesSetStartPayloadSchema,
|
||||
)(params, { onExcessProperty: "error" });
|
||||
return `uv run wf draft set-start ${shellArg(p.workspace_id)} --revision ${p.revision} --step ${shellArg(p.step_id)}`;
|
||||
},
|
||||
interpret: (result) => {
|
||||
const decoded = Schema.decodeUnknownSync(
|
||||
WorkflowDraftWorkspacesSetStartResultSchema,
|
||||
)(result);
|
||||
return interpretDraftWorkspace(decoded);
|
||||
},
|
||||
},
|
||||
{
|
||||
method: "workflow.draft_workspaces.set_step_input_bindings",
|
||||
label: "Set step input bindings",
|
||||
@@ -788,6 +938,36 @@ const operationEntries = defineOperationEntries([
|
||||
return interpretDraftWorkspace(decoded);
|
||||
},
|
||||
},
|
||||
{
|
||||
method: "workflow.draft_workspaces.set_workflow_output_bindings",
|
||||
label: "Set workflow output bindings",
|
||||
explanation: "Replace the ordered public workflow output bindings",
|
||||
idempotency: "write",
|
||||
equivalentCli: (params) => {
|
||||
const p = Schema.decodeUnknownSync(
|
||||
WorkflowDraftWorkspacesSetWorkflowOutputBindingsPayloadSchema,
|
||||
)(params, { onExcessProperty: "error" });
|
||||
const parts = [
|
||||
"uv run wf draft set-workflow-output",
|
||||
shellArg(p.workspace_id),
|
||||
"--revision",
|
||||
String(p.revision),
|
||||
];
|
||||
if (p.bindings.length === 0) {
|
||||
parts.push("--clear");
|
||||
return parts.join(" ");
|
||||
}
|
||||
const rendered = inputBindingCliArgs(p.bindings, "--map");
|
||||
parts.push(...rendered.args);
|
||||
return nonEquivalentCli(parts.join(" "), rendered.unavailable);
|
||||
},
|
||||
interpret: (result) => {
|
||||
const decoded = Schema.decodeUnknownSync(
|
||||
WorkflowDraftWorkspacesSetWorkflowOutputBindingsResultSchema,
|
||||
)(result);
|
||||
return interpretDraftWorkspace(decoded);
|
||||
},
|
||||
},
|
||||
{
|
||||
method: "workflow.draft_workspaces.validate",
|
||||
label: "Validate draft workspace",
|
||||
|
||||
Reference in New Issue
Block a user