@contentful/experience-design-system-cli 2.12.0 → 2.12.1-dev-build-589b615.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/README.md +214 -357
- package/dist/package.json +1 -1
- package/dist/src/analyze/extract/astro.js +9 -0
- package/dist/src/analyze/extract/react.js +23 -0
- package/dist/src/analyze/extract/stencil.js +3 -0
- package/dist/src/analyze/extract/vue.js +6 -0
- package/dist/src/analyze/extract/web-components.js +10 -0
- package/dist/src/analyze/pre-classify.d.ts +1 -1
- package/dist/src/analyze/pre-classify.js +3 -3
- package/dist/src/analyze/select/persistence.d.ts +13 -0
- package/dist/src/analyze/select/persistence.js +38 -0
- package/dist/src/analyze/select/preview-annotations.d.ts +26 -0
- package/dist/src/analyze/select/preview-annotations.js +62 -0
- package/dist/src/analyze/select/tui/App.js +5 -2
- package/dist/src/analyze/select/tui/components/ComponentRationalePanel.d.ts +30 -0
- package/dist/src/analyze/select/tui/components/ComponentRationalePanel.js +137 -0
- package/dist/src/analyze/select/tui/components/FieldEditor.d.ts +58 -1
- package/dist/src/analyze/select/tui/components/FieldEditor.js +637 -161
- package/dist/src/analyze/select/tui/components/FinalizeDialog.js +1 -1
- package/dist/src/analyze/select/tui/components/JsonPanel.js +5 -1
- package/dist/src/analyze/select/tui/components/RationalePanel.d.ts +34 -0
- package/dist/src/analyze/select/tui/components/RationalePanel.js +92 -0
- package/dist/src/analyze/select/tui/components/Sidebar.d.ts +13 -1
- package/dist/src/analyze/select/tui/components/Sidebar.js +27 -3
- package/dist/src/analyze/select/tui/hooks/scroll-offset.d.ts +29 -0
- package/dist/src/analyze/select/tui/hooks/scroll-offset.js +26 -0
- package/dist/src/analyze/select-agent/command.d.ts +2 -0
- package/dist/src/analyze/select-agent/command.js +259 -45
- package/dist/src/analyze/select-agent/show-rationale.d.ts +35 -0
- package/dist/src/analyze/select-agent/show-rationale.js +92 -0
- package/dist/src/apply/command.js +7 -3
- package/dist/src/apply/tui/ServerApplyView.d.ts +3 -1
- package/dist/src/apply/tui/ServerApplyView.js +3 -2
- package/dist/src/credentials-store.d.ts +5 -0
- package/dist/src/credentials-store.js +7 -1
- package/dist/src/generate/agent-runner.d.ts +14 -0
- package/dist/src/generate/agent-runner.js +16 -0
- package/dist/src/generate/command.d.ts +6 -0
- package/dist/src/generate/command.js +45 -10
- package/dist/src/generate/progress.d.ts +9 -0
- package/dist/src/generate/progress.js +11 -0
- package/dist/src/generate/prompt-builder.d.ts +8 -0
- package/dist/src/generate/prompt-builder.js +22 -11
- package/dist/src/import/agent-model-resolve.d.ts +23 -0
- package/dist/src/import/agent-model-resolve.js +35 -0
- package/dist/src/import/auto-filter-resolve.d.ts +12 -0
- package/dist/src/import/auto-filter-resolve.js +16 -0
- package/dist/src/import/command.js +248 -8
- package/dist/src/import/orchestrator.d.ts +2 -0
- package/dist/src/import/orchestrator.js +11 -3
- package/dist/src/import/print-prompt.d.ts +35 -0
- package/dist/src/import/print-prompt.js +31 -0
- package/dist/src/import/tui/CustomPromptBanner.d.ts +11 -0
- package/dist/src/import/tui/CustomPromptBanner.js +7 -0
- package/dist/src/import/tui/WizardApp.d.ts +112 -8
- package/dist/src/import/tui/WizardApp.js +814 -333
- package/dist/src/import/tui/auto-filter-error.d.ts +2 -0
- package/dist/src/import/tui/auto-filter-error.js +29 -0
- package/dist/src/import/tui/final-review-host.d.ts +15 -0
- package/dist/src/import/tui/final-review-host.js +20 -0
- package/dist/src/import/tui/push-decision-gate-helpers.d.ts +8 -0
- package/dist/src/import/tui/push-decision-gate-helpers.js +8 -0
- package/dist/src/import/tui/push-progress.d.ts +23 -0
- package/dist/src/import/tui/push-progress.js +14 -0
- package/dist/src/import/tui/run-print-files-helpers.d.ts +26 -0
- package/dist/src/import/tui/run-print-files-helpers.js +8 -0
- package/dist/src/import/tui/run-teaser.d.ts +5 -0
- package/dist/src/import/tui/run-teaser.js +9 -0
- package/dist/src/import/tui/runLivePreview.d.ts +35 -0
- package/dist/src/import/tui/runLivePreview.js +73 -0
- package/dist/src/import/tui/runScopeGate.d.ts +22 -0
- package/dist/src/import/tui/runScopeGate.js +28 -0
- package/dist/src/import/tui/scope-gate-host.d.ts +21 -0
- package/dist/src/import/tui/scope-gate-host.js +20 -0
- package/dist/src/import/tui/spawn-generate.d.ts +33 -0
- package/dist/src/import/tui/spawn-generate.js +53 -0
- package/dist/src/import/tui/steps/CredentialsStep.d.ts +25 -1
- package/dist/src/import/tui/steps/CredentialsStep.js +31 -3
- package/dist/src/import/tui/steps/DoneStep.d.ts +6 -1
- package/dist/src/import/tui/steps/DoneStep.js +10 -4
- package/dist/src/import/tui/steps/GenerateReviewStep.d.ts +29 -2
- package/dist/src/import/tui/steps/GenerateReviewStep.js +412 -48
- package/dist/src/import/tui/steps/PushDecisionGateStep.d.ts +19 -0
- package/dist/src/import/tui/steps/PushDecisionGateStep.js +76 -0
- package/dist/src/import/tui/steps/PushingStep.d.ts +10 -0
- package/dist/src/import/tui/steps/PushingStep.js +40 -0
- package/dist/src/import/tui/steps/ScopeGateStep.d.ts +23 -0
- package/dist/src/import/tui/steps/ScopeGateStep.js +243 -0
- package/dist/src/import/tui/steps/TokenInputStep.js +20 -3
- package/dist/src/import/tui/useLivePreview.d.ts +35 -0
- package/dist/src/import/tui/useLivePreview.js +120 -0
- package/dist/src/import/tui/wizard-generate-progress.d.ts +21 -0
- package/dist/src/import/tui/wizard-generate-progress.js +25 -0
- package/dist/src/import/tui/wizard-save-flow.d.ts +37 -0
- package/dist/src/import/tui/wizard-save-flow.js +20 -0
- package/dist/src/import/tui/wizard-state-transitions.d.ts +80 -0
- package/dist/src/import/tui/wizard-state-transitions.js +79 -0
- package/dist/src/lib/contentful-urls.d.ts +34 -0
- package/dist/src/lib/contentful-urls.js +41 -0
- package/dist/src/program.js +2 -0
- package/dist/src/runs/export-helpers.d.ts +30 -0
- package/dist/src/runs/export-helpers.js +59 -0
- package/dist/src/runs/fingerprint.d.ts +47 -0
- package/dist/src/runs/fingerprint.js +75 -0
- package/dist/src/runs/ls-command.d.ts +12 -0
- package/dist/src/runs/ls-command.js +137 -0
- package/dist/src/runs/modify-launcher.d.ts +24 -0
- package/dist/src/runs/modify-launcher.js +36 -0
- package/dist/src/runs/path-prompt.d.ts +8 -0
- package/dist/src/runs/path-prompt.js +72 -0
- package/dist/src/runs/push-creds-prompt.d.ts +25 -0
- package/dist/src/runs/push-creds-prompt.js +39 -0
- package/dist/src/runs/push-helpers.d.ts +29 -0
- package/dist/src/runs/push-helpers.js +69 -0
- package/dist/src/runs/replay-helpers.d.ts +67 -0
- package/dist/src/runs/replay-helpers.js +148 -0
- package/dist/src/runs/resolve-run-target.d.ts +12 -0
- package/dist/src/runs/resolve-run-target.js +45 -0
- package/dist/src/runs/run-picker-mount.d.ts +46 -0
- package/dist/src/runs/run-picker-mount.js +72 -0
- package/dist/src/runs/run-picker.d.ts +17 -0
- package/dist/src/runs/run-picker.js +145 -0
- package/dist/src/runs/save-conflict.d.ts +8 -0
- package/dist/src/runs/save-conflict.js +56 -0
- package/dist/src/runs/save-path-resolver.d.ts +55 -0
- package/dist/src/runs/save-path-resolver.js +71 -0
- package/dist/src/runs/staleness.d.ts +38 -0
- package/dist/src/runs/staleness.js +140 -0
- package/dist/src/runs/store.d.ts +78 -0
- package/dist/src/runs/store.js +136 -0
- package/dist/src/session/cache-keys.d.ts +15 -0
- package/dist/src/session/cache-keys.js +38 -0
- package/dist/src/session/db.d.ts +89 -3
- package/dist/src/session/db.js +447 -104
- package/dist/src/setup/auto-filter-prompt.d.ts +13 -0
- package/dist/src/setup/auto-filter-prompt.js +24 -0
- package/dist/src/setup/command.d.ts +7 -0
- package/dist/src/setup/command.js +58 -2
- package/dist/src/types.d.ts +6 -0
- package/package.json +2 -2
- package/skills/generate-components.md +54 -6
- package/skills/select-components.md +13 -4
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Pure helpers for shaping the auto-filter subprocess stderr stream into a
|
|
2
|
+
// human-readable error tail. The auto-filter (select-agent) subprocess emits
|
|
3
|
+
// three classes of lines on stderr:
|
|
4
|
+
//
|
|
5
|
+
// 1. `progress=...` machine-parseable progress lines consumed by the wizard
|
|
6
|
+
// to drive `aiDecisions` / `aiFilterProgress` state.
|
|
7
|
+
// 2. `[N/total] Name <verb> ...` per-component status lines (human-readable
|
|
8
|
+
// mirror of the progress emission).
|
|
9
|
+
// 3. Free-form error output — what we actually want to surface on failure.
|
|
10
|
+
//
|
|
11
|
+
// `buildAutoFilterErrorTail` strips classes (1) and (2) plus ANSI color
|
|
12
|
+
// escapes, then returns the last 3 lines joined with " / " (matching the
|
|
13
|
+
// pre-existing tail format in WizardApp.tsx).
|
|
14
|
+
const ANSI_RE = /\x1b\[[0-9;]*m/g;
|
|
15
|
+
const PROGRESS_RE = /^progress=/;
|
|
16
|
+
const PER_COMPONENT_RE = /\[\d+\/\d+\]/;
|
|
17
|
+
export function stripAnsi(s) {
|
|
18
|
+
return s.replace(ANSI_RE, '');
|
|
19
|
+
}
|
|
20
|
+
function isStructured(line) {
|
|
21
|
+
return PROGRESS_RE.test(line) || PER_COMPONENT_RE.test(line);
|
|
22
|
+
}
|
|
23
|
+
export function buildAutoFilterErrorTail(raw) {
|
|
24
|
+
const lines = raw
|
|
25
|
+
.split('\n')
|
|
26
|
+
.map((l) => stripAnsi(l.trim()))
|
|
27
|
+
.filter((l) => l.length > 0 && !isStructured(l));
|
|
28
|
+
return lines.slice(-3).join(' / ');
|
|
29
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export type FinalReviewHostProps = {
|
|
3
|
+
extractSessionId: string | null;
|
|
4
|
+
generatedCount: number;
|
|
5
|
+
autoAccept: boolean;
|
|
6
|
+
onFinalize: (accepted: number, rejected: number, unresolved: number) => void;
|
|
7
|
+
onQuit: () => void;
|
|
8
|
+
livePreview?: boolean;
|
|
9
|
+
spaceId?: string;
|
|
10
|
+
environmentId?: string;
|
|
11
|
+
cmaToken?: string;
|
|
12
|
+
host?: string;
|
|
13
|
+
tokensPath?: string;
|
|
14
|
+
};
|
|
15
|
+
export declare function FinalReviewHost({ extractSessionId, generatedCount, autoAccept, onFinalize, onQuit, livePreview, spaceId, environmentId, cmaToken, host, tokensPath, }: FinalReviewHostProps): React.ReactElement;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { GenerateReviewStep } from './steps/GenerateReviewStep.js';
|
|
5
|
+
export function FinalReviewHost({ extractSessionId, generatedCount, autoAccept, onFinalize, onQuit, livePreview, spaceId, environmentId, cmaToken, host, tokensPath, }) {
|
|
6
|
+
if (!extractSessionId) {
|
|
7
|
+
return (_jsx(Box, { paddingX: 2, paddingY: 1, children: _jsx(Text, { color: "red", children: "Error: no session ID \u2014 cannot load generated definitions." }) }));
|
|
8
|
+
}
|
|
9
|
+
if (autoAccept) {
|
|
10
|
+
return _jsx(FinalReviewAutoAccept, { generatedCount: generatedCount, onFinalize: onFinalize });
|
|
11
|
+
}
|
|
12
|
+
return (_jsx(GenerateReviewStep, { extractSessionId: extractSessionId, onFinalize: onFinalize, onQuit: onQuit, livePreview: livePreview, spaceId: spaceId, environmentId: environmentId, cmaToken: cmaToken, host: host, tokensPath: tokensPath }));
|
|
13
|
+
}
|
|
14
|
+
function FinalReviewAutoAccept({ generatedCount, onFinalize, }) {
|
|
15
|
+
React.useEffect(() => {
|
|
16
|
+
onFinalize(generatedCount, 0, 0);
|
|
17
|
+
// fire once on mount; deps intentionally empty so a re-render with new generatedCount doesn't double-finalize
|
|
18
|
+
}, []);
|
|
19
|
+
return (_jsx(Box, { paddingX: 2, paddingY: 1, children: _jsxs(Text, { dimColor: true, children: ["Auto-accepting ", generatedCount, " generated components..."] }) }));
|
|
20
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { PushDecisionChoice } from './steps/PushDecisionGateStep.js';
|
|
2
|
+
export type GateAction = 'save-and-push' | 'push-only' | 'save-only';
|
|
3
|
+
/**
|
|
4
|
+
* Maps a `PushDecisionGateStep` choice to the wizard action it triggers.
|
|
5
|
+
* Centralizing this keeps the gate component free of wizard-side semantics
|
|
6
|
+
* and gives us a unit-testable seam between UI choice and side-effect.
|
|
7
|
+
*/
|
|
8
|
+
export declare function chooseGateAction(choice: PushDecisionChoice): GateAction;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maps a `PushDecisionGateStep` choice to the wizard action it triggers.
|
|
3
|
+
* Centralizing this keeps the gate component free of wizard-side semantics
|
|
4
|
+
* and gives us a unit-testable seam between UI choice and side-effect.
|
|
5
|
+
*/
|
|
6
|
+
export function chooseGateAction(choice) {
|
|
7
|
+
return choice === 'both' ? 'save-and-push' : choice;
|
|
8
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ServerPreviewResponse } from '@contentful/experience-design-system-types';
|
|
2
|
+
export type PushExpected = {
|
|
3
|
+
componentTypes: {
|
|
4
|
+
create: number;
|
|
5
|
+
update: number;
|
|
6
|
+
remove: number;
|
|
7
|
+
};
|
|
8
|
+
designTokens: {
|
|
9
|
+
create: number;
|
|
10
|
+
update: number;
|
|
11
|
+
remove: number;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
export type PushProgress = {
|
|
15
|
+
kind: 'queued';
|
|
16
|
+
operationId: string;
|
|
17
|
+
} | {
|
|
18
|
+
kind: 'progress';
|
|
19
|
+
processed: number;
|
|
20
|
+
total: number;
|
|
21
|
+
current: string | null;
|
|
22
|
+
} | null;
|
|
23
|
+
export declare function computePushExpected(preview: ServerPreviewResponse): PushExpected;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export function computePushExpected(preview) {
|
|
2
|
+
return {
|
|
3
|
+
componentTypes: {
|
|
4
|
+
create: preview.components?.new?.length ?? 0,
|
|
5
|
+
update: preview.components?.changed?.length ?? 0,
|
|
6
|
+
remove: preview.components?.removed?.length ?? 0,
|
|
7
|
+
},
|
|
8
|
+
designTokens: {
|
|
9
|
+
create: preview.tokens?.new?.length ?? 0,
|
|
10
|
+
update: preview.tokens?.changed?.length ?? 0,
|
|
11
|
+
remove: preview.tokens?.removed?.length ?? 0,
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Computes the wizard state patch applied after `runPrintFiles` successfully
|
|
3
|
+
* writes components.json. Default behavior transitions to the print-gate
|
|
4
|
+
* confirm screen. When `skipGate: true` is passed (used by the "save AND push"
|
|
5
|
+
* path), the transition is suppressed so the caller can chain into
|
|
6
|
+
* `runPreview` without an intermediate gate render.
|
|
7
|
+
*
|
|
8
|
+
* When `outDir` is provided (Task 4 — operator chose a custom save path via
|
|
9
|
+
* the inline path-prompt), it is folded into the patch so the wizard's
|
|
10
|
+
* `state.outDir` reflects the chosen path. Subsequent reads (e.g. the
|
|
11
|
+
* appendRun call, the print-gate's summary) pick it up automatically.
|
|
12
|
+
*/
|
|
13
|
+
export type NextStateAfterPrintInput = {
|
|
14
|
+
skipGate?: boolean;
|
|
15
|
+
componentsPath: string;
|
|
16
|
+
outDir?: string;
|
|
17
|
+
};
|
|
18
|
+
export type NextStateAfterPrintOutput = {
|
|
19
|
+
componentsPath: string;
|
|
20
|
+
outDir?: string;
|
|
21
|
+
} | {
|
|
22
|
+
step: 'print-gate';
|
|
23
|
+
componentsPath: string;
|
|
24
|
+
outDir?: string;
|
|
25
|
+
};
|
|
26
|
+
export declare function nextStateAfterPrint(opts: NextStateAfterPrintInput): NextStateAfterPrintOutput;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task 8 — one-line teaser appended at the end of every successful run.
|
|
3
|
+
* Format pinned in dsi-tui-local-save-tooling-spec.md Part 6.
|
|
4
|
+
*/
|
|
5
|
+
export function buildRunTeaserLine(runId) {
|
|
6
|
+
if (!runId)
|
|
7
|
+
return '';
|
|
8
|
+
return `Run saved as ${runId} — push to Contentful with 'experiences import --push-from-run ${runId}' or modify with 'experiences import --modify ${runId}'.`;
|
|
9
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { ServerPreviewResponse } from '@contentful/experience-design-system-types';
|
|
2
|
+
export declare class TimeoutError extends Error {
|
|
3
|
+
constructor(message?: string);
|
|
4
|
+
}
|
|
5
|
+
export type LivePreviewResult = {
|
|
6
|
+
generation: number;
|
|
7
|
+
response: ServerPreviewResponse | null;
|
|
8
|
+
};
|
|
9
|
+
export type RunLivePreviewOptions = {
|
|
10
|
+
sessionId: string;
|
|
11
|
+
tokensPath: string;
|
|
12
|
+
spaceId: string;
|
|
13
|
+
environmentId: string;
|
|
14
|
+
cmaToken: string;
|
|
15
|
+
host: string;
|
|
16
|
+
generation: number;
|
|
17
|
+
timeoutMs?: number;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Pure async helper used by `useLivePreview` to re-fire `previewImport` after a
|
|
21
|
+
* FieldEditor save in the wizard's final-review step. Mirrors the round-1
|
|
22
|
+
* `runScopeGate` shape: a side-effect-bearing pure orchestrator that owns no
|
|
23
|
+
* React state.
|
|
24
|
+
*
|
|
25
|
+
* Skips silently (returns `{ response: null }`) when any of `spaceId`,
|
|
26
|
+
* `environmentId`, or `cmaToken` is missing — live preview is an enhancement
|
|
27
|
+
* and the caller treats `null` as a no-op. Throws on API errors so the caller
|
|
28
|
+
* (`useLivePreview`) can downgrade to local-only mode on 401/403 and log a
|
|
29
|
+
* warning otherwise. Wraps the API call in a timeout so a hung backend doesn't
|
|
30
|
+
* leave the spinner stuck.
|
|
31
|
+
*
|
|
32
|
+
* Does NOT run the second-pass seeding from `WizardApp.runPreview`; that's a
|
|
33
|
+
* one-shot wizard-step concern and would re-seed on every save.
|
|
34
|
+
*/
|
|
35
|
+
export declare function runLivePreview(opts: RunLivePreviewOptions): Promise<LivePreviewResult>;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { buildManifest } from '@contentful/experience-design-system-types';
|
|
2
|
+
import { ImportApiClient } from '../../apply/api-client.js';
|
|
3
|
+
import { readTokensFromPath } from '../../apply/manifest.js';
|
|
4
|
+
import { backfillUnclassifiedProps, loadCDFComponents, openPipelineDb } from '../../session/db.js';
|
|
5
|
+
export class TimeoutError extends Error {
|
|
6
|
+
constructor(message = 'live-preview timed out') {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = 'TimeoutError';
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
const DEFAULT_TIMEOUT_MS = 15_000;
|
|
12
|
+
/**
|
|
13
|
+
* Pure async helper used by `useLivePreview` to re-fire `previewImport` after a
|
|
14
|
+
* FieldEditor save in the wizard's final-review step. Mirrors the round-1
|
|
15
|
+
* `runScopeGate` shape: a side-effect-bearing pure orchestrator that owns no
|
|
16
|
+
* React state.
|
|
17
|
+
*
|
|
18
|
+
* Skips silently (returns `{ response: null }`) when any of `spaceId`,
|
|
19
|
+
* `environmentId`, or `cmaToken` is missing — live preview is an enhancement
|
|
20
|
+
* and the caller treats `null` as a no-op. Throws on API errors so the caller
|
|
21
|
+
* (`useLivePreview`) can downgrade to local-only mode on 401/403 and log a
|
|
22
|
+
* warning otherwise. Wraps the API call in a timeout so a hung backend doesn't
|
|
23
|
+
* leave the spinner stuck.
|
|
24
|
+
*
|
|
25
|
+
* Does NOT run the second-pass seeding from `WizardApp.runPreview`; that's a
|
|
26
|
+
* one-shot wizard-step concern and would re-seed on every save.
|
|
27
|
+
*/
|
|
28
|
+
export async function runLivePreview(opts) {
|
|
29
|
+
const { sessionId, tokensPath, spaceId, environmentId, cmaToken, host, generation } = opts;
|
|
30
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
31
|
+
if (!spaceId || !environmentId || !cmaToken) {
|
|
32
|
+
return { generation, response: null };
|
|
33
|
+
}
|
|
34
|
+
let components = [];
|
|
35
|
+
if (sessionId) {
|
|
36
|
+
const db = openPipelineDb();
|
|
37
|
+
try {
|
|
38
|
+
backfillUnclassifiedProps(db, sessionId);
|
|
39
|
+
components = loadCDFComponents(db, sessionId);
|
|
40
|
+
}
|
|
41
|
+
finally {
|
|
42
|
+
db.close();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
let tokens = [];
|
|
46
|
+
if (tokensPath) {
|
|
47
|
+
tokens = await readTokensFromPath('tokens', tokensPath);
|
|
48
|
+
}
|
|
49
|
+
const manifest = buildManifest(components, tokens);
|
|
50
|
+
const client = new ImportApiClient({ host, cmaToken, spaceId, environmentId });
|
|
51
|
+
const startedAt = Date.now();
|
|
52
|
+
let timeoutHandle;
|
|
53
|
+
const timeoutPromise = new Promise((_resolve, reject) => {
|
|
54
|
+
timeoutHandle = setTimeout(() => reject(new TimeoutError()), timeoutMs);
|
|
55
|
+
});
|
|
56
|
+
try {
|
|
57
|
+
const response = (await Promise.race([client.previewImport(manifest), timeoutPromise]));
|
|
58
|
+
if (process.env['EDS_VERBOSE']) {
|
|
59
|
+
const durationMs = Date.now() - startedAt;
|
|
60
|
+
try {
|
|
61
|
+
process.stderr.write(`live-preview: ${durationMs}ms\n`);
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// best-effort
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return { generation, response };
|
|
68
|
+
}
|
|
69
|
+
finally {
|
|
70
|
+
if (timeoutHandle)
|
|
71
|
+
clearTimeout(timeoutHandle);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export declare function runScopeGate(opts: {
|
|
2
|
+
sessionId: string;
|
|
3
|
+
decisions: {
|
|
4
|
+
accepted: string[];
|
|
5
|
+
rejected: string[];
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Optional cancellation hook for the auto-filter (`select-agent`) subprocess.
|
|
9
|
+
* If supplied, it will be awaited BEFORE the review-state snapshot is written
|
|
10
|
+
* so the operator's full decision set is the last writer to the snapshot
|
|
11
|
+
* file. Without this gate the subprocess can race the operator's confirm
|
|
12
|
+
* (see PR #43): if select-agent finishes a batch after the operator presses
|
|
13
|
+
* `f`, it overwrites the snapshot with a partial view and components silently
|
|
14
|
+
* disappear from final-review. Resolves when the subprocess has exited.
|
|
15
|
+
*/
|
|
16
|
+
cancelAutoFilter?: () => Promise<void> | void;
|
|
17
|
+
onAdvanceToGenerate: (info: {
|
|
18
|
+
sessionId: string;
|
|
19
|
+
acceptedCount: number;
|
|
20
|
+
}) => Promise<void> | void;
|
|
21
|
+
onAdvanceToPushFlow: (acceptedCount: number) => Promise<void> | void;
|
|
22
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { writeScopeDecisionsSnapshot } from '../../analyze/select/persistence.js';
|
|
2
|
+
import { applyScopeDecisions, openPipelineDb } from '../../session/db.js';
|
|
3
|
+
export async function runScopeGate(opts) {
|
|
4
|
+
// Cancel-and-await BEFORE any DB / snapshot write. The subprocess's
|
|
5
|
+
// in-flight write may complete before SIGTERM lands; that's fine — we wait
|
|
6
|
+
// for it to fully exit, then our authoritative write goes last.
|
|
7
|
+
if (opts.cancelAutoFilter) {
|
|
8
|
+
await opts.cancelAutoFilter();
|
|
9
|
+
}
|
|
10
|
+
const db = openPipelineDb();
|
|
11
|
+
try {
|
|
12
|
+
applyScopeDecisions(db, opts.sessionId, opts.decisions);
|
|
13
|
+
// Also persist a review-state snapshot so `generate components` can filter
|
|
14
|
+
// out rejected components via loadAcceptedNames. Without this the wizard's
|
|
15
|
+
// scope-gate decisions never reach the generator and rejected components
|
|
16
|
+
// get processed by the LLM anyway.
|
|
17
|
+
await writeScopeDecisionsSnapshot(db, opts.sessionId, opts.decisions);
|
|
18
|
+
}
|
|
19
|
+
finally {
|
|
20
|
+
db.close();
|
|
21
|
+
}
|
|
22
|
+
if (opts.decisions.accepted.length > 0) {
|
|
23
|
+
await opts.onAdvanceToGenerate({ sessionId: opts.sessionId, acceptedCount: opts.decisions.accepted.length });
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
await opts.onAdvanceToPushFlow(0);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ScopeComponent } from './steps/ScopeGateStep.js';
|
|
3
|
+
export type { ScopeComponent };
|
|
4
|
+
export type AutoFilterStatus = 'idle' | 'running' | 'complete' | 'cancelled' | 'failed';
|
|
5
|
+
export type ScopeGateHostProps = {
|
|
6
|
+
components: ReadonlyArray<ScopeComponent>;
|
|
7
|
+
autoAccept: boolean;
|
|
8
|
+
onConfirm: (decisions: {
|
|
9
|
+
accepted: string[];
|
|
10
|
+
rejected: string[];
|
|
11
|
+
}) => void;
|
|
12
|
+
onQuit: () => void;
|
|
13
|
+
aiFilterStatus?: AutoFilterStatus;
|
|
14
|
+
aiFilterProgress?: {
|
|
15
|
+
done: number;
|
|
16
|
+
total: number;
|
|
17
|
+
} | null;
|
|
18
|
+
aiFilterError?: string | null;
|
|
19
|
+
onCancelAutoFilter?: () => void;
|
|
20
|
+
};
|
|
21
|
+
export declare function ScopeGateHost({ components, autoAccept, onConfirm, onQuit, aiFilterStatus, aiFilterProgress, aiFilterError, onCancelAutoFilter, }: ScopeGateHostProps): React.ReactElement;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { ScopeGateStep } from './steps/ScopeGateStep.js';
|
|
5
|
+
export function ScopeGateHost({ components, autoAccept, onConfirm, onQuit, aiFilterStatus = 'idle', aiFilterProgress = null, aiFilterError = null, onCancelAutoFilter, }) {
|
|
6
|
+
if (components.length === 0) {
|
|
7
|
+
return (_jsx(Box, { paddingX: 2, paddingY: 1, children: _jsx(Text, { color: "red", children: "Error: no components found for this session \u2014 please re-run analyze extract." }) }));
|
|
8
|
+
}
|
|
9
|
+
if (autoAccept) {
|
|
10
|
+
return _jsx(ScopeGateAutoAccept, { components: components, onConfirm: onConfirm });
|
|
11
|
+
}
|
|
12
|
+
return (_jsx(ScopeGateStep, { components: [...components], onConfirm: onConfirm, onQuit: onQuit, aiFilterStatus: aiFilterStatus, aiFilterProgress: aiFilterProgress, aiFilterError: aiFilterError, onCancelAutoFilter: onCancelAutoFilter }));
|
|
13
|
+
}
|
|
14
|
+
function ScopeGateAutoAccept({ components, onConfirm, }) {
|
|
15
|
+
React.useEffect(() => {
|
|
16
|
+
onConfirm({ accepted: components.map((c) => c.name), rejected: [] });
|
|
17
|
+
// fire once on mount; deps intentionally empty so a re-render with new components doesn't double-confirm
|
|
18
|
+
}, []);
|
|
19
|
+
return (_jsx(Box, { paddingX: 2, paddingY: 1, children: _jsxs(Text, { dimColor: true, children: ["Auto-accepting ", components.length, " components..."] }) }));
|
|
20
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extracted spawn helper for the `generate components` subprocess.
|
|
3
|
+
*
|
|
4
|
+
* Why this exists: the wizard now kicks off generation from TWO call sites —
|
|
5
|
+
* the post-credentials path (`runGenerate`) AND a pre-fetch from scope-gate
|
|
6
|
+
* confirm (Change 2 in the prefetch spec). Both need the same spawn-and-stream
|
|
7
|
+
* behavior but the await-on-exit happens at different times. Keeping `runGenerate`
|
|
8
|
+
* as a thin wrapper around this helper lets the pre-fetch path stash the
|
|
9
|
+
* `donePromise` in a ref and resolve it later.
|
|
10
|
+
*
|
|
11
|
+
* Subprocess hygiene: we listen for 'close' (not 'exit') when surfacing the
|
|
12
|
+
* final result so the child's stdio + any inherited file handles (e.g. the
|
|
13
|
+
* pipeline SQLite WAL/SHM) have been fully released by the OS before the
|
|
14
|
+
* wizard re-enters that path. This applies the same lesson as
|
|
15
|
+
* fix/auto-filter-db-lock.
|
|
16
|
+
*/
|
|
17
|
+
import { type ChildProcess } from 'node:child_process';
|
|
18
|
+
export type SpawnGenerateResult = {
|
|
19
|
+
exitCode: number;
|
|
20
|
+
signal: NodeJS.Signals | null;
|
|
21
|
+
stdout: string;
|
|
22
|
+
stderr: string;
|
|
23
|
+
};
|
|
24
|
+
export type SpawnGenerateOpts = {
|
|
25
|
+
command: string;
|
|
26
|
+
args: string[];
|
|
27
|
+
onStderr?: (chunk: string) => void;
|
|
28
|
+
onStdout?: (chunk: string) => void;
|
|
29
|
+
};
|
|
30
|
+
export declare function spawnGenerateChild(opts: SpawnGenerateOpts): {
|
|
31
|
+
child: ChildProcess;
|
|
32
|
+
donePromise: Promise<SpawnGenerateResult>;
|
|
33
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extracted spawn helper for the `generate components` subprocess.
|
|
3
|
+
*
|
|
4
|
+
* Why this exists: the wizard now kicks off generation from TWO call sites —
|
|
5
|
+
* the post-credentials path (`runGenerate`) AND a pre-fetch from scope-gate
|
|
6
|
+
* confirm (Change 2 in the prefetch spec). Both need the same spawn-and-stream
|
|
7
|
+
* behavior but the await-on-exit happens at different times. Keeping `runGenerate`
|
|
8
|
+
* as a thin wrapper around this helper lets the pre-fetch path stash the
|
|
9
|
+
* `donePromise` in a ref and resolve it later.
|
|
10
|
+
*
|
|
11
|
+
* Subprocess hygiene: we listen for 'close' (not 'exit') when surfacing the
|
|
12
|
+
* final result so the child's stdio + any inherited file handles (e.g. the
|
|
13
|
+
* pipeline SQLite WAL/SHM) have been fully released by the OS before the
|
|
14
|
+
* wizard re-enters that path. This applies the same lesson as
|
|
15
|
+
* fix/auto-filter-db-lock.
|
|
16
|
+
*/
|
|
17
|
+
import { spawn } from 'node:child_process';
|
|
18
|
+
export function spawnGenerateChild(opts) {
|
|
19
|
+
const child = spawn(opts.command, opts.args);
|
|
20
|
+
let stdout = '';
|
|
21
|
+
let stderr = '';
|
|
22
|
+
child.stdout?.on('data', (d) => {
|
|
23
|
+
const chunk = String(d);
|
|
24
|
+
stdout += chunk;
|
|
25
|
+
opts.onStdout?.(chunk);
|
|
26
|
+
});
|
|
27
|
+
child.stderr?.on('data', (d) => {
|
|
28
|
+
const chunk = String(d);
|
|
29
|
+
stderr += chunk;
|
|
30
|
+
opts.onStderr?.(chunk);
|
|
31
|
+
});
|
|
32
|
+
const donePromise = new Promise((resolve) => {
|
|
33
|
+
// Use 'close' rather than 'exit' so stdio drains and any inherited file
|
|
34
|
+
// handles are released before downstream code reacts.
|
|
35
|
+
child.on('close', (code, signal) => {
|
|
36
|
+
resolve({
|
|
37
|
+
exitCode: code ?? 0,
|
|
38
|
+
signal: signal ?? null,
|
|
39
|
+
stdout,
|
|
40
|
+
stderr,
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
child.on('error', (err) => {
|
|
44
|
+
resolve({
|
|
45
|
+
exitCode: 1,
|
|
46
|
+
signal: null,
|
|
47
|
+
stdout,
|
|
48
|
+
stderr: stderr + (err.message ?? String(err)),
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
return { child, donePromise };
|
|
53
|
+
}
|
|
@@ -6,11 +6,35 @@ type CredentialsStepProps = {
|
|
|
6
6
|
initialEnvironmentId?: string;
|
|
7
7
|
initialCmaToken?: string;
|
|
8
8
|
initialHost?: string;
|
|
9
|
+
/**
|
|
10
|
+
* When true, the credentials screen stays mounted but locks input and shows
|
|
11
|
+
* an inline "Validating credentials..." status. Replaces the previous
|
|
12
|
+
* dedicated `validating-credentials` render screen so the operator sees a
|
|
13
|
+
* continuous credentials surface instead of a transient loading screen
|
|
14
|
+
* (Change 1 of the wizard prefetch refactor).
|
|
15
|
+
*/
|
|
16
|
+
validating?: boolean;
|
|
17
|
+
/** Inline status describing in-flight background generation prefetch. */
|
|
18
|
+
generatePrefetchStatus?: 'idle' | 'running' | 'complete' | 'failed';
|
|
19
|
+
/** Error message from a failed generation prefetch (rendered as a banner). */
|
|
20
|
+
generatePrefetchError?: string | null;
|
|
9
21
|
/** Called when the user submits with any field changed from its initial value */
|
|
10
22
|
onConfirm: (spaceId: string, environmentId: string, cmaToken: string, host: string) => void;
|
|
11
23
|
/** Called when the user submits without changing any field (use existing creds as-is) */
|
|
12
24
|
onContinue?: (spaceId: string, environmentId: string, cmaToken: string, host: string) => void;
|
|
13
25
|
onQuit: () => void;
|
|
26
|
+
/**
|
|
27
|
+
* Optional retry callback wired up when a background generation prefetch
|
|
28
|
+
* failed mid-credentials-entry. The operator presses R to re-trigger.
|
|
29
|
+
*/
|
|
30
|
+
onRetryPrefetch?: () => void;
|
|
31
|
+
/**
|
|
32
|
+
* Skip-credentials escape hatch. When pressed (via `s` keybind, gated
|
|
33
|
+
* against text-entry mode), the wizard advances without validating
|
|
34
|
+
* credentials and disables push downstream. See dsi-tui-skip-credentials
|
|
35
|
+
* spec.
|
|
36
|
+
*/
|
|
37
|
+
onSkip?: () => void;
|
|
14
38
|
};
|
|
15
|
-
export declare function CredentialsStep({ summary, error: externalError, initialSpaceId, initialEnvironmentId, initialCmaToken, initialHost, onConfirm, onContinue, onQuit, }: CredentialsStepProps): React.ReactElement;
|
|
39
|
+
export declare function CredentialsStep({ summary, error: externalError, initialSpaceId, initialEnvironmentId, initialCmaToken, initialHost, validating, generatePrefetchStatus, generatePrefetchError, onConfirm, onContinue, onQuit, onRetryPrefetch, onSkip, }: CredentialsStepProps): React.ReactElement;
|
|
16
40
|
export {};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState, useEffect } from 'react';
|
|
2
|
+
import { useState, useEffect, useRef } from 'react';
|
|
3
3
|
import { Box, Text } from 'ink';
|
|
4
4
|
import { useImmediateInput } from '../../../analyze/select/tui/hooks/useImmediateInput.js';
|
|
5
5
|
import { DEFAULT_CONFIGURED_HOST, toConfiguredHost } from '../../../host-utils.js';
|
|
6
|
-
export function CredentialsStep({ summary, error: externalError, initialSpaceId = '', initialEnvironmentId = 'master', initialCmaToken = '', initialHost, onConfirm, onContinue, onQuit, }) {
|
|
6
|
+
export function CredentialsStep({ summary, error: externalError, initialSpaceId = '', initialEnvironmentId = 'master', initialCmaToken = '', initialHost, validating = false, generatePrefetchStatus = 'idle', generatePrefetchError = null, onConfirm, onContinue, onQuit, onRetryPrefetch, onSkip, }) {
|
|
7
7
|
const normalizedInitialHost = toConfiguredHost(initialHost) ?? DEFAULT_CONFIGURED_HOST;
|
|
8
8
|
const [spaceId, setSpaceId] = useState(initialSpaceId);
|
|
9
9
|
const [environmentId, setEnvironmentId] = useState(initialEnvironmentId);
|
|
@@ -12,11 +12,28 @@ export function CredentialsStep({ summary, error: externalError, initialSpaceId
|
|
|
12
12
|
const [activeField, setActiveField] = useState('spaceId');
|
|
13
13
|
const [inlineError, setInlineError] = useState(null);
|
|
14
14
|
const [cursorVisible, setCursorVisible] = useState(true);
|
|
15
|
+
// Tracks whether the operator has typed any printable character into a
|
|
16
|
+
// form field since mount. While false, the `s` keybind is interpreted as
|
|
17
|
+
// the skip-credentials shortcut. Once the operator has begun typing, `s`
|
|
18
|
+
// is routed into the active field as input (we don't want to swallow a
|
|
19
|
+
// legitimate letter in a space ID / token / host).
|
|
20
|
+
const hasTypedRef = useRef(false);
|
|
15
21
|
useEffect(() => {
|
|
16
22
|
const interval = setInterval(() => setCursorVisible((v) => !v), 500);
|
|
17
23
|
return () => clearInterval(interval);
|
|
18
24
|
}, []);
|
|
19
25
|
useImmediateInput((input, key) => {
|
|
26
|
+
// While we are validating credentials, the screen stays mounted but the
|
|
27
|
+
// form is locked — any input is dropped. The exception is `R` when a
|
|
28
|
+
// prefetch failed and we expose a retry hook (so the operator can recover
|
|
29
|
+
// without backing out of the wizard).
|
|
30
|
+
if (validating) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if ((input === 'r' || input === 'R') && generatePrefetchStatus === 'failed' && onRetryPrefetch) {
|
|
34
|
+
onRetryPrefetch();
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
20
37
|
if (key.return) {
|
|
21
38
|
if (activeField === 'spaceId') {
|
|
22
39
|
setActiveField('environmentId');
|
|
@@ -57,6 +74,14 @@ export function CredentialsStep({ summary, error: externalError, initialSpaceId
|
|
|
57
74
|
onQuit();
|
|
58
75
|
return;
|
|
59
76
|
}
|
|
77
|
+
// Skip-credentials shortcut. Gated against text-entry mode so the letter
|
|
78
|
+
// 's' can still be typed into a form field once the operator has begun
|
|
79
|
+
// editing. The legend hint is always rendered (see below) so operators
|
|
80
|
+
// know the escape hatch exists from the moment the screen mounts.
|
|
81
|
+
if ((input === 's' || input === 'S') && onSkip && !hasTypedRef.current) {
|
|
82
|
+
onSkip();
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
60
85
|
if (key.backspace || key.delete) {
|
|
61
86
|
if (activeField === 'spaceId')
|
|
62
87
|
setSpaceId((v) => v.slice(0, -1));
|
|
@@ -69,6 +94,7 @@ export function CredentialsStep({ summary, error: externalError, initialSpaceId
|
|
|
69
94
|
return;
|
|
70
95
|
}
|
|
71
96
|
if (input && !key.ctrl && !key.meta) {
|
|
97
|
+
hasTypedRef.current = true;
|
|
72
98
|
if (activeField === 'spaceId')
|
|
73
99
|
setSpaceId((v) => v + input);
|
|
74
100
|
else if (activeField === 'environmentId')
|
|
@@ -89,5 +115,7 @@ export function CredentialsStep({ summary, error: externalError, initialSpaceId
|
|
|
89
115
|
const displayError = inlineError ?? externalError ?? null;
|
|
90
116
|
return (_jsxs(Box, { flexDirection: "column", gap: 1, paddingX: 2, paddingY: 1, children: [summary && _jsxs(Text, { color: "green", children: ["\u2713 ", summary] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { children: initialSpaceId && initialCmaToken
|
|
91
117
|
? 'Credentials pre-filled from experiences setup. Press Enter to continue or edit any field to update.'
|
|
92
|
-
: 'Enter your Contentful credentials to continue.' }) }), !(initialSpaceId && initialCmaToken) && (_jsx(Text, { dimColor: true, children: "Tip: run experiences setup to save these to ~/.config/experiences/credentials.json so they pre-fill here automatically." })), _jsxs(Box, { flexDirection: "column", gap: 0, marginTop: 1, children: [renderField('Space ID', spaceId, 'spaceId'), renderField('Environment', environmentId, 'environmentId'), renderField('CMA Token', cmaToken, 'cmaToken', true), renderField('API Host', host, 'host')] }), activeField === 'host' && _jsx(Text, { dimColor: true, children: "Default: api.contentful.com \u00B7 EU spaces: api.eu.contentful.com" }), displayError && _jsxs(Text, { color: "red", children: ["\u2717 ", displayError] }),
|
|
118
|
+
: 'Enter your Contentful credentials to continue.' }) }), !(initialSpaceId && initialCmaToken) && (_jsx(Text, { dimColor: true, children: "Tip: run experiences setup to save these to ~/.config/experiences/credentials.json so they pre-fill here automatically." })), _jsxs(Box, { flexDirection: "column", gap: 0, marginTop: 1, children: [renderField('Space ID', spaceId, 'spaceId'), renderField('Environment', environmentId, 'environmentId'), renderField('CMA Token', cmaToken, 'cmaToken', true), renderField('API Host', host, 'host')] }), activeField === 'host' && _jsx(Text, { dimColor: true, children: "Default: api.contentful.com \u00B7 EU spaces: api.eu.contentful.com" }), displayError && _jsxs(Text, { color: "red", children: ["\u2717 ", displayError] }), validating && (_jsx(Text, { color: "cyan", children: generatePrefetchStatus === 'running'
|
|
119
|
+
? 'Validating credentials & finishing component generation...'
|
|
120
|
+
: 'Validating credentials...' })), !validating && generatePrefetchStatus === 'running' && _jsx(Text, { dimColor: true, children: "Component generation in progress..." }), !validating && generatePrefetchStatus === 'complete' && (_jsx(Text, { color: "green", children: "Component generation complete." })), !validating && generatePrefetchStatus === 'failed' && (_jsxs(Text, { color: "yellow", children: ["Component generation failed", generatePrefetchError ? `: ${generatePrefetchError}` : '', ". Will retry after credential validation.", onRetryPrefetch ? ' Press R to retry now.' : ''] })), _jsxs(Box, { gap: 3, marginTop: 1, children: [_jsx(Text, { dimColor: true, children: "[Enter] Next field / Submit" }), _jsx(Text, { dimColor: true, children: "[Tab] Switch field" }), _jsx(Text, { dimColor: true, children: "[q] Quit" })] }), onSkip && (_jsx(Box, { marginTop: 0, children: _jsx(Text, { dimColor: true, children: "[s] Skip \u2014 review locally only (no push, no live preview)" }) }))] }));
|
|
93
121
|
}
|
|
@@ -2,6 +2,7 @@ import React from 'react';
|
|
|
2
2
|
type EntityResult = {
|
|
3
3
|
created: number;
|
|
4
4
|
updated: number;
|
|
5
|
+
removed: number;
|
|
5
6
|
failed: number;
|
|
6
7
|
};
|
|
7
8
|
type DoneStepProps = {
|
|
@@ -14,7 +15,11 @@ type DoneStepProps = {
|
|
|
14
15
|
};
|
|
15
16
|
spaceId: string;
|
|
16
17
|
environmentId: string;
|
|
18
|
+
/** Configured API host (e.g. `api.contentful.com`). Used to derive the webapp URL. */
|
|
19
|
+
host?: string;
|
|
20
|
+
/** Task 8 — pre-formatted run teaser; rendered dim below the space link. */
|
|
21
|
+
runTeaser?: string;
|
|
17
22
|
onExit: () => void;
|
|
18
23
|
};
|
|
19
|
-
export declare function DoneStep({ componentTypes, designTokens, summary, spaceId, environmentId, onExit, }: DoneStepProps): React.ReactElement;
|
|
24
|
+
export declare function DoneStep({ componentTypes, designTokens, summary, spaceId, environmentId, host, runTeaser, onExit, }: DoneStepProps): React.ReactElement;
|
|
20
25
|
export {};
|