@contentful/experience-design-system-cli 2.26.1-dev-build-a852362.0 → 2.26.1-dev-build-f03f014.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/package.json +14 -10
- package/dist/src/index.js +34937 -9
- package/dist/src/lib/cli-path.d.ts +4 -0
- package/dist/src/runs/export-helpers.d.ts +1 -12
- package/dist/src/runs/push-helpers.d.ts +1 -6
- package/package.json +12 -7
- package/skills/generate-components.md +427 -0
- package/skills/generate-tokens.md +194 -0
- package/skills/select-components.md +224 -0
- package/dist/src/analytics/apply.js +0 -30
- package/dist/src/analytics/client.js +0 -81
- package/dist/src/analytics/constants.js +0 -4
- package/dist/src/analytics/env.js +0 -14
- package/dist/src/analytics/exit.js +0 -24
- package/dist/src/analytics/index.js +0 -9
- package/dist/src/analytics/normalize.js +0 -18
- package/dist/src/analytics/os.js +0 -13
- package/dist/src/analytics/session.js +0 -15
- package/dist/src/analytics/tracker.js +0 -126
- package/dist/src/analytics/types.js +0 -1
- package/dist/src/analyze/build-analyze-view-rows.js +0 -63
- package/dist/src/analyze/command.js +0 -599
- package/dist/src/analyze/composite-closure.js +0 -0
- package/dist/src/analyze/composition/agent-parser/author-prompt.js +0 -22
- package/dist/src/analyze/composition/agent-parser/extract-parser.js +0 -44
- package/dist/src/analyze/composition/agent-parser/load-prompt.js +0 -31
- package/dist/src/analyze/composition/agent-parser/resolve-via-parser.js +0 -88
- package/dist/src/analyze/composition/agent-parser/sandbox.js +0 -187
- package/dist/src/analyze/composition/apply-mapping.js +0 -67
- package/dist/src/analyze/composition/candidate-critic-agent.js +0 -39
- package/dist/src/analyze/composition/candidate-critic.js +0 -63
- package/dist/src/analyze/composition/candidate-files.js +0 -131
- package/dist/src/analyze/composition/composition-cache-key.js +0 -19
- package/dist/src/analyze/composition/interchange-schema.js +0 -61
- package/dist/src/analyze/composition/manifest-doc-evidence.js +0 -136
- package/dist/src/analyze/composition/merge-edges.js +0 -70
- package/dist/src/analyze/composition/parse-map-edges.js +0 -55
- package/dist/src/analyze/composition/resolve-mapping-cli.js +0 -29
- package/dist/src/analyze/composition/resolve-mapping.js +0 -111
- package/dist/src/analyze/cycle-detection.js +0 -234
- package/dist/src/analyze/cycle-view.js +0 -28
- package/dist/src/analyze/fuzzy-search.js +0 -55
- package/dist/src/analyze/issue-inheritance.js +0 -108
- package/dist/src/analyze/lineage.js +0 -0
- package/dist/src/analyze/scope-gate-cascade.js +0 -170
- package/dist/src/analyze/search-neighborhood.js +0 -56
- package/dist/src/analyze/select/command.js +0 -484
- package/dist/src/analyze/select/index.js +0 -5
- package/dist/src/analyze/select/parser.js +0 -53
- package/dist/src/analyze/select/persistence.js +0 -80
- package/dist/src/analyze/select/preview-annotations.js +0 -62
- package/dist/src/analyze/select/stdout.js +0 -3
- package/dist/src/analyze/select/tui/App.js +0 -483
- package/dist/src/analyze/select/tui/components/ComponentDetail.js +0 -48
- package/dist/src/analyze/select/tui/components/ComponentRationalePanel.js +0 -125
- package/dist/src/analyze/select/tui/components/FieldEditor.js +0 -1131
- package/dist/src/analyze/select/tui/components/FinalizeDialog.js +0 -36
- package/dist/src/analyze/select/tui/components/GotoBanner.js +0 -30
- package/dist/src/analyze/select/tui/components/GroupedSidebar.js +0 -517
- package/dist/src/analyze/select/tui/components/HelpOverlay.js +0 -17
- package/dist/src/analyze/select/tui/components/JsonEditor.js +0 -131
- package/dist/src/analyze/select/tui/components/JsonPanel.js +0 -66
- package/dist/src/analyze/select/tui/components/LineagePanel.js +0 -22
- package/dist/src/analyze/select/tui/components/PreviewSummaryBar.js +0 -29
- package/dist/src/analyze/select/tui/components/QuitDialog.js +0 -14
- package/dist/src/analyze/select/tui/components/RationalePanel.js +0 -80
- package/dist/src/analyze/select/tui/components/Sidebar.js +0 -92
- package/dist/src/analyze/select/tui/components/SourcePanel.js +0 -52
- package/dist/src/analyze/select/tui/components/StatusBar.js +0 -7
- package/dist/src/analyze/select/tui/components/TopBar.js +0 -10
- package/dist/src/analyze/select/tui/components/removed-components-text.js +0 -11
- package/dist/src/analyze/select/tui/hooks/scroll-offset.js +0 -26
- package/dist/src/analyze/select/tui/hooks/useImmediateInput.js +0 -66
- package/dist/src/analyze/select/tui/hooks/useKeymap.js +0 -67
- package/dist/src/analyze/select/tui/hooks/useSession.js +0 -52
- package/dist/src/analyze/select/tui/hooks/useUndo.js +0 -26
- package/dist/src/analyze/select/tui/theme.js +0 -28
- package/dist/src/analyze/select/types.js +0 -39
- package/dist/src/analyze/select-agent/command.js +0 -558
- package/dist/src/analyze/select-agent/context-builder.js +0 -197
- package/dist/src/analyze/select-agent/show-rationale.js +0 -92
- package/dist/src/analyze/selection-cascade.js +0 -19
- package/dist/src/analyze/slot-graph.js +0 -17
- package/dist/src/analyze/tui/AnalyzeView.js +0 -64
- package/dist/src/apply/api-client.js +0 -440
- package/dist/src/apply/command.js +0 -845
- package/dist/src/apply/manifest.js +0 -1
- package/dist/src/apply/preview-utils.js +0 -26
- package/dist/src/apply/tui/SelectView.js +0 -34
- package/dist/src/apply/tui/ServerApplyView.js +0 -49
- package/dist/src/apply/tui/ServerPreviewView.js +0 -23
- package/dist/src/credentials-store.js +0 -74
- package/dist/src/generate/command.js +0 -597
- package/dist/src/generate/edit/command.js +0 -127
- package/dist/src/generate/tui/GenerateView.js +0 -10
- package/dist/src/host-utils.js +0 -26
- package/dist/src/import/agent-model-resolve.js +0 -35
- package/dist/src/import/auto-filter-resolve.js +0 -16
- package/dist/src/import/command.js +0 -380
- package/dist/src/import/cycle-auto-reject.js +0 -14
- package/dist/src/import/orchestrator.js +0 -603
- package/dist/src/import/path-utils.js +0 -30
- package/dist/src/import/picker-dispatch.js +0 -44
- package/dist/src/import/print-prompt.js +0 -31
- package/dist/src/import/strip-allowed-components.js +0 -22
- package/dist/src/import/tui/CustomPromptBanner.js +0 -8
- package/dist/src/import/tui/WizardApp.js +0 -1670
- package/dist/src/import/tui/ai-flag.js +0 -6
- package/dist/src/import/tui/auto-filter-error.js +0 -29
- package/dist/src/import/tui/autocomplete.js +0 -32
- package/dist/src/import/tui/components/AutoFilterBanner.js +0 -19
- package/dist/src/import/tui/components/CounterStrip.js +0 -12
- package/dist/src/import/tui/components/LegendEntry.js +0 -6
- package/dist/src/import/tui/cycle-panel-scroll.js +0 -33
- package/dist/src/import/tui/final-review-host.js +0 -25
- package/dist/src/import/tui/group-collapse.js +0 -11
- package/dist/src/import/tui/history.js +0 -53
- package/dist/src/import/tui/hooks/useLineage.js +0 -50
- package/dist/src/import/tui/hooks/useOverlayPanel.js +0 -27
- package/dist/src/import/tui/lineage-layout.js +0 -82
- package/dist/src/import/tui/merge-ai-decisions.js +0 -22
- package/dist/src/import/tui/push-decision-gate-helpers.js +0 -8
- package/dist/src/import/tui/push-progress.js +0 -1
- package/dist/src/import/tui/run-print-files-helpers.js +0 -8
- package/dist/src/import/tui/run-teaser.js +0 -9
- package/dist/src/import/tui/runLivePreview.js +0 -82
- package/dist/src/import/tui/runScopeGate.js +0 -28
- package/dist/src/import/tui/scope-gate-columns.js +0 -94
- package/dist/src/import/tui/scope-gate-host.js +0 -28
- package/dist/src/import/tui/sidebar-width.js +0 -3
- package/dist/src/import/tui/spawn-generate.js +0 -53
- package/dist/src/import/tui/step-filters.js +0 -42
- package/dist/src/import/tui/steps/AtomicGenerateReviewStep.js +0 -726
- package/dist/src/import/tui/steps/AtomicScopeGateStep.js +0 -210
- package/dist/src/import/tui/steps/CredentialsStep.js +0 -108
- package/dist/src/import/tui/steps/DoneStep.js +0 -24
- package/dist/src/import/tui/steps/ErrorStep.js +0 -16
- package/dist/src/import/tui/steps/GateStep.js +0 -22
- package/dist/src/import/tui/steps/GenerateReviewStep.js +0 -1618
- package/dist/src/import/tui/steps/PathValidationStep.js +0 -152
- package/dist/src/import/tui/steps/PreviewStep.js +0 -37
- package/dist/src/import/tui/steps/PreviewValidationErrorStep.js +0 -23
- package/dist/src/import/tui/steps/PushDecisionGateStep.js +0 -73
- package/dist/src/import/tui/steps/PushingStep.js +0 -24
- package/dist/src/import/tui/steps/RunningStep.js +0 -21
- package/dist/src/import/tui/steps/ScopeGateStep.js +0 -907
- package/dist/src/import/tui/steps/TokenInputStep.js +0 -88
- package/dist/src/import/tui/steps/WelcomeStep.js +0 -34
- package/dist/src/import/tui/steps/WizardPreviewStep.js +0 -169
- package/dist/src/import/tui/steps/auto-reject-decision.js +0 -9
- package/dist/src/import/tui/steps/breaking-change-format.js +0 -24
- package/dist/src/import/tui/steps/enumerate-cycle-breaks.js +0 -0
- package/dist/src/import/tui/steps/preview-diff.js +0 -175
- package/dist/src/import/tui/useFinalizePreview.js +0 -0
- package/dist/src/import/tui/useLivePreview.js +0 -122
- package/dist/src/import/tui/wizard-422-helpers.js +0 -60
- package/dist/src/import/tui/wizard-generate-progress.js +0 -25
- package/dist/src/import/tui/wizard-save-flow.js +0 -20
- package/dist/src/import/tui/wizard-state-transitions.js +0 -32
- package/dist/src/lib/agent-model-options.js +0 -15
- package/dist/src/lib/command-options.js +0 -30
- package/dist/src/lib/composition-mode.js +0 -39
- package/dist/src/lib/contentful-urls.js +0 -27
- package/dist/src/lib/debug-logger.js +0 -229
- package/dist/src/lib/debug-preamble.js +0 -33
- package/dist/src/lib/error-parser.js +0 -269
- package/dist/src/lib/prompt-overrides.js +0 -69
- package/dist/src/lib/terminal-capabilities.js +0 -51
- package/dist/src/lib/user-agent.js +0 -29
- package/dist/src/output/format.js +0 -110
- package/dist/src/print/command.js +0 -221
- package/dist/src/print/validate/tui/ValidateView.js +0 -37
- package/dist/src/print/validate/validators/cdf-validator.js +0 -104
- package/dist/src/print/validate/validators/dtcg-validator.js +0 -110
- package/dist/src/print/validate/validators/format-errors.js +0 -18
- package/dist/src/program.js +0 -105
- package/dist/src/runs/export-helpers.js +0 -59
- package/dist/src/runs/fingerprint.js +0 -75
- package/dist/src/runs/ls-command.js +0 -137
- package/dist/src/runs/modify-launcher.js +0 -42
- package/dist/src/runs/path-prompt.js +0 -72
- package/dist/src/runs/push-creds-prompt.js +0 -39
- package/dist/src/runs/push-helpers.js +0 -72
- package/dist/src/runs/push-launcher.js +0 -77
- package/dist/src/runs/replay-helpers.js +0 -164
- package/dist/src/runs/resolve-run-target.js +0 -45
- package/dist/src/runs/run-picker-mount.js +0 -72
- package/dist/src/runs/run-picker.js +0 -129
- package/dist/src/runs/save-conflict.js +0 -56
- package/dist/src/runs/save-path-resolver.js +0 -75
- package/dist/src/runs/staleness.js +0 -140
- package/dist/src/runs/store.js +0 -136
- package/dist/src/session/cache-keys.js +0 -44
- package/dist/src/session/command.js +0 -261
- package/dist/src/session/db.js +0 -1538
- package/dist/src/session/migration.js +0 -117
- package/dist/src/session/session-id.js +0 -212
- package/dist/src/session/stats.js +0 -89
- package/dist/src/setup/analytics-prompt.js +0 -24
- package/dist/src/setup/auto-filter-prompt.js +0 -24
- package/dist/src/setup/command.js +0 -966
- package/dist/src/setup/composition-mode-prompt.js +0 -37
- package/dist/src/setup/debug-mode-prompt.js +0 -22
- package/dist/src/types.js +0 -1
package/dist/src/session/db.js
DELETED
|
@@ -1,1538 +0,0 @@
|
|
|
1
|
-
import { DatabaseSync } from 'node:sqlite';
|
|
2
|
-
import { mkdirSync, readFileSync } from 'node:fs';
|
|
3
|
-
import { createHash } from 'node:crypto';
|
|
4
|
-
import { homedir } from 'node:os';
|
|
5
|
-
import { dirname, resolve } from 'node:path';
|
|
6
|
-
import { generateSessionId } from './session-id.js';
|
|
7
|
-
const SCHEMA = `
|
|
8
|
-
PRAGMA journal_mode = WAL;
|
|
9
|
-
PRAGMA foreign_keys = ON;
|
|
10
|
-
|
|
11
|
-
CREATE TABLE IF NOT EXISTS migrations (
|
|
12
|
-
name TEXT NOT NULL PRIMARY KEY,
|
|
13
|
-
applied_at TEXT NOT NULL
|
|
14
|
-
);
|
|
15
|
-
|
|
16
|
-
CREATE TABLE IF NOT EXISTS sessions (
|
|
17
|
-
id TEXT NOT NULL PRIMARY KEY,
|
|
18
|
-
name TEXT,
|
|
19
|
-
created_at TEXT NOT NULL,
|
|
20
|
-
updated_at TEXT NOT NULL
|
|
21
|
-
);
|
|
22
|
-
|
|
23
|
-
CREATE TABLE IF NOT EXISTS steps (
|
|
24
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
25
|
-
session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
26
|
-
command TEXT NOT NULL,
|
|
27
|
-
status TEXT NOT NULL DEFAULT 'pending',
|
|
28
|
-
started_at TEXT NOT NULL,
|
|
29
|
-
completed_at TEXT,
|
|
30
|
-
inputs TEXT NOT NULL DEFAULT '{}',
|
|
31
|
-
outputs TEXT NOT NULL DEFAULT '{}',
|
|
32
|
-
error TEXT,
|
|
33
|
-
updated_at TEXT NOT NULL
|
|
34
|
-
);
|
|
35
|
-
|
|
36
|
-
CREATE TABLE IF NOT EXISTS raw_components (
|
|
37
|
-
session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
38
|
-
component_id TEXT NOT NULL,
|
|
39
|
-
name TEXT NOT NULL,
|
|
40
|
-
source TEXT NOT NULL,
|
|
41
|
-
framework TEXT NOT NULL,
|
|
42
|
-
extracted_at TEXT NOT NULL,
|
|
43
|
-
status TEXT NOT NULL DEFAULT 'extracted',
|
|
44
|
-
cdf_schema TEXT,
|
|
45
|
-
description TEXT,
|
|
46
|
-
extraction_confidence INTEGER,
|
|
47
|
-
review_reasons TEXT NOT NULL DEFAULT '[]',
|
|
48
|
-
needs_review INTEGER NOT NULL DEFAULT 0,
|
|
49
|
-
source_path TEXT,
|
|
50
|
-
reject_reason TEXT,
|
|
51
|
-
PRIMARY KEY (session_id, component_id)
|
|
52
|
-
);
|
|
53
|
-
|
|
54
|
-
CREATE TABLE IF NOT EXISTS raw_props (
|
|
55
|
-
session_id TEXT NOT NULL,
|
|
56
|
-
component_id TEXT NOT NULL,
|
|
57
|
-
name TEXT NOT NULL,
|
|
58
|
-
type TEXT NOT NULL,
|
|
59
|
-
required INTEGER NOT NULL CHECK (required IN (0, 1)),
|
|
60
|
-
category TEXT CHECK (category IN ('content', 'design', 'state')),
|
|
61
|
-
default_value TEXT,
|
|
62
|
-
description TEXT,
|
|
63
|
-
token_reference TEXT,
|
|
64
|
-
position INTEGER NOT NULL,
|
|
65
|
-
cdf_type TEXT,
|
|
66
|
-
cdf_category TEXT CHECK (cdf_category IN ('content', 'design', 'state')),
|
|
67
|
-
cdf_token_kind TEXT,
|
|
68
|
-
rationale TEXT,
|
|
69
|
-
source_start_line INTEGER,
|
|
70
|
-
source_end_line INTEGER,
|
|
71
|
-
PRIMARY KEY (session_id, component_id, name),
|
|
72
|
-
FOREIGN KEY (session_id, component_id) REFERENCES raw_components(session_id, component_id) ON DELETE CASCADE
|
|
73
|
-
);
|
|
74
|
-
|
|
75
|
-
CREATE TABLE IF NOT EXISTS raw_prop_allowed_values (
|
|
76
|
-
session_id TEXT NOT NULL,
|
|
77
|
-
component_id TEXT NOT NULL,
|
|
78
|
-
prop_name TEXT NOT NULL,
|
|
79
|
-
position INTEGER NOT NULL,
|
|
80
|
-
value TEXT NOT NULL,
|
|
81
|
-
PRIMARY KEY (session_id, component_id, prop_name, position),
|
|
82
|
-
FOREIGN KEY (session_id, component_id, prop_name) REFERENCES raw_props(session_id, component_id, name) ON DELETE CASCADE
|
|
83
|
-
);
|
|
84
|
-
|
|
85
|
-
CREATE TABLE IF NOT EXISTS raw_slots (
|
|
86
|
-
session_id TEXT NOT NULL,
|
|
87
|
-
component_id TEXT NOT NULL,
|
|
88
|
-
name TEXT NOT NULL,
|
|
89
|
-
is_default INTEGER NOT NULL CHECK (is_default IN (0, 1)),
|
|
90
|
-
description TEXT,
|
|
91
|
-
position INTEGER NOT NULL,
|
|
92
|
-
required INTEGER NOT NULL DEFAULT 1 CHECK (required IN (0, 1)),
|
|
93
|
-
PRIMARY KEY (session_id, component_id, name),
|
|
94
|
-
FOREIGN KEY (session_id, component_id) REFERENCES raw_components(session_id, component_id) ON DELETE CASCADE
|
|
95
|
-
);
|
|
96
|
-
|
|
97
|
-
CREATE TABLE IF NOT EXISTS raw_slot_allowed_components (
|
|
98
|
-
session_id TEXT NOT NULL,
|
|
99
|
-
component_id TEXT NOT NULL,
|
|
100
|
-
slot_name TEXT NOT NULL,
|
|
101
|
-
position INTEGER NOT NULL,
|
|
102
|
-
allowed_component TEXT NOT NULL,
|
|
103
|
-
PRIMARY KEY (session_id, component_id, slot_name, position),
|
|
104
|
-
FOREIGN KEY (session_id, component_id, slot_name) REFERENCES raw_slots(session_id, component_id, name) ON DELETE CASCADE
|
|
105
|
-
);
|
|
106
|
-
|
|
107
|
-
CREATE TABLE IF NOT EXISTS raw_token_groups (
|
|
108
|
-
session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
109
|
-
path TEXT NOT NULL,
|
|
110
|
-
description TEXT,
|
|
111
|
-
PRIMARY KEY (session_id, path)
|
|
112
|
-
);
|
|
113
|
-
|
|
114
|
-
CREATE TABLE IF NOT EXISTS raw_tokens (
|
|
115
|
-
session_id TEXT NOT NULL,
|
|
116
|
-
path TEXT NOT NULL,
|
|
117
|
-
type TEXT NOT NULL,
|
|
118
|
-
value TEXT NOT NULL,
|
|
119
|
-
description TEXT,
|
|
120
|
-
PRIMARY KEY (session_id, path),
|
|
121
|
-
FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE
|
|
122
|
-
);
|
|
123
|
-
|
|
124
|
-
CREATE TABLE IF NOT EXISTS generation_cache (
|
|
125
|
-
input_hash TEXT NOT NULL,
|
|
126
|
-
entity_type TEXT NOT NULL CHECK (entity_type IN ('component', 'token_set')),
|
|
127
|
-
entity_id TEXT NOT NULL,
|
|
128
|
-
source_session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
129
|
-
human_edited INTEGER NOT NULL DEFAULT 0 CHECK (human_edited IN (0, 1)),
|
|
130
|
-
created_at TEXT NOT NULL,
|
|
131
|
-
updated_at TEXT NOT NULL,
|
|
132
|
-
PRIMARY KEY (input_hash, entity_type, entity_id)
|
|
133
|
-
);
|
|
134
|
-
|
|
135
|
-
CREATE TABLE IF NOT EXISTS scanned_files (
|
|
136
|
-
session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
137
|
-
path TEXT NOT NULL,
|
|
138
|
-
PRIMARY KEY (session_id, path)
|
|
139
|
-
);
|
|
140
|
-
|
|
141
|
-
CREATE TABLE IF NOT EXISTS slot_cycles (
|
|
142
|
-
session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
143
|
-
cycle_index INTEGER NOT NULL,
|
|
144
|
-
path_json TEXT NOT NULL,
|
|
145
|
-
edges_json TEXT NOT NULL,
|
|
146
|
-
suggested_break_json TEXT,
|
|
147
|
-
PRIMARY KEY (session_id, cycle_index)
|
|
148
|
-
);
|
|
149
|
-
|
|
150
|
-
CREATE INDEX IF NOT EXISTS idx_steps_session ON steps(session_id);
|
|
151
|
-
CREATE INDEX IF NOT EXISTS idx_steps_command ON steps(session_id, command);
|
|
152
|
-
CREATE INDEX IF NOT EXISTS idx_raw_components_session ON raw_components(session_id);
|
|
153
|
-
CREATE INDEX IF NOT EXISTS idx_raw_props_session ON raw_props(session_id, component_id);
|
|
154
|
-
CREATE INDEX IF NOT EXISTS idx_raw_slots_session ON raw_slots(session_id, component_id);
|
|
155
|
-
CREATE INDEX IF NOT EXISTS idx_raw_tokens_session ON raw_tokens(session_id);
|
|
156
|
-
CREATE INDEX IF NOT EXISTS idx_raw_token_groups_session ON raw_token_groups(session_id);
|
|
157
|
-
CREATE INDEX IF NOT EXISTS idx_generation_cache_entity ON generation_cache(entity_type, entity_id);
|
|
158
|
-
CREATE INDEX IF NOT EXISTS idx_generation_cache_session ON generation_cache(source_session_id);
|
|
159
|
-
CREATE INDEX IF NOT EXISTS idx_scanned_files_session ON scanned_files(session_id);
|
|
160
|
-
`;
|
|
161
|
-
export function getPipelineDbPath() {
|
|
162
|
-
if (process.env.EDS_PIPELINE_DB_PATH) {
|
|
163
|
-
return resolve(process.env.EDS_PIPELINE_DB_PATH);
|
|
164
|
-
}
|
|
165
|
-
return resolve(homedir(), '.contentful', 'experience-design-system-cli', 'pipeline.db');
|
|
166
|
-
}
|
|
167
|
-
export function openPipelineDb(dbPath) {
|
|
168
|
-
const path = dbPath ?? getPipelineDbPath();
|
|
169
|
-
mkdirSync(dirname(path), { recursive: true });
|
|
170
|
-
try {
|
|
171
|
-
const db = new DatabaseSync(path);
|
|
172
|
-
db.exec('PRAGMA busy_timeout = 5000');
|
|
173
|
-
db.exec('PRAGMA journal_mode = WAL');
|
|
174
|
-
db.exec('PRAGMA synchronous = NORMAL');
|
|
175
|
-
db.exec(SCHEMA);
|
|
176
|
-
applyDbMigrations(db);
|
|
177
|
-
return db;
|
|
178
|
-
}
|
|
179
|
-
catch (e) {
|
|
180
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
181
|
-
if (msg.includes('database is locked')) {
|
|
182
|
-
throw new Error('database is locked: another CLI process may be running. Retry once it exits.');
|
|
183
|
-
}
|
|
184
|
-
throw e;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
function applyDbMigrations(db) {
|
|
188
|
-
const cols = db.prepare('PRAGMA table_info(raw_slots)').all();
|
|
189
|
-
if (!cols.some((c) => c.name === 'required')) {
|
|
190
|
-
db.exec('ALTER TABLE raw_slots ADD COLUMN required INTEGER NOT NULL DEFAULT 1 CHECK (required IN (0, 1))');
|
|
191
|
-
}
|
|
192
|
-
const rawCompCols = db.prepare('PRAGMA table_info(raw_components)').all();
|
|
193
|
-
const rawCompColNames = new Set(rawCompCols.map((c) => c.name));
|
|
194
|
-
if (!rawCompColNames.has('extraction_confidence')) {
|
|
195
|
-
db.exec('ALTER TABLE raw_components ADD COLUMN extraction_confidence INTEGER');
|
|
196
|
-
}
|
|
197
|
-
if (!rawCompColNames.has('review_reasons')) {
|
|
198
|
-
db.exec("ALTER TABLE raw_components ADD COLUMN review_reasons TEXT NOT NULL DEFAULT '[]'");
|
|
199
|
-
}
|
|
200
|
-
if (!rawCompColNames.has('needs_review')) {
|
|
201
|
-
db.exec('ALTER TABLE raw_components ADD COLUMN needs_review INTEGER NOT NULL DEFAULT 0');
|
|
202
|
-
}
|
|
203
|
-
if (!rawCompColNames.has('source_path')) {
|
|
204
|
-
db.exec('ALTER TABLE raw_components ADD COLUMN source_path TEXT');
|
|
205
|
-
}
|
|
206
|
-
const rawPropCols = db.prepare('PRAGMA table_info(raw_props)').all();
|
|
207
|
-
const rawPropColNames = new Set(rawPropCols.map((c) => c.name));
|
|
208
|
-
if (!rawPropColNames.has('rationale')) {
|
|
209
|
-
db.exec('ALTER TABLE raw_props ADD COLUMN rationale TEXT');
|
|
210
|
-
}
|
|
211
|
-
if (!rawPropColNames.has('source_start_line')) {
|
|
212
|
-
db.exec('ALTER TABLE raw_props ADD COLUMN source_start_line INTEGER');
|
|
213
|
-
}
|
|
214
|
-
if (!rawPropColNames.has('source_end_line')) {
|
|
215
|
-
db.exec('ALTER TABLE raw_props ADD COLUMN source_end_line INTEGER');
|
|
216
|
-
}
|
|
217
|
-
if (!rawCompColNames.has('reject_reason')) {
|
|
218
|
-
db.exec('ALTER TABLE raw_components ADD COLUMN reject_reason TEXT');
|
|
219
|
-
}
|
|
220
|
-
if (!rawCompColNames.has('component_description_rationale')) {
|
|
221
|
-
db.exec('ALTER TABLE raw_components ADD COLUMN component_description_rationale TEXT');
|
|
222
|
-
}
|
|
223
|
-
if (!rawCompColNames.has('props_rationale')) {
|
|
224
|
-
db.exec('ALTER TABLE raw_components ADD COLUMN props_rationale TEXT');
|
|
225
|
-
}
|
|
226
|
-
if (!rawCompColNames.has('slots_rationale')) {
|
|
227
|
-
db.exec('ALTER TABLE raw_components ADD COLUMN slots_rationale TEXT');
|
|
228
|
-
}
|
|
229
|
-
const rawSlotColsForRationale = db.prepare('PRAGMA table_info(raw_slots)').all();
|
|
230
|
-
if (!rawSlotColsForRationale.some((c) => c.name === 'rationale')) {
|
|
231
|
-
db.exec('ALTER TABLE raw_slots ADD COLUMN rationale TEXT');
|
|
232
|
-
}
|
|
233
|
-
const hasExtractCache = db
|
|
234
|
-
.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='extract_cache'`)
|
|
235
|
-
.all();
|
|
236
|
-
if (hasExtractCache.length === 0) {
|
|
237
|
-
db.exec(`
|
|
238
|
-
CREATE TABLE extract_cache (
|
|
239
|
-
file_path TEXT NOT NULL,
|
|
240
|
-
file_hash TEXT NOT NULL,
|
|
241
|
-
cli_version TEXT NOT NULL,
|
|
242
|
-
created_at TEXT NOT NULL,
|
|
243
|
-
updated_at TEXT NOT NULL,
|
|
244
|
-
components_json TEXT NOT NULL,
|
|
245
|
-
PRIMARY KEY (file_hash, cli_version)
|
|
246
|
-
);
|
|
247
|
-
CREATE INDEX idx_extract_cache_file ON extract_cache(file_path);
|
|
248
|
-
`);
|
|
249
|
-
}
|
|
250
|
-
const hasSelectCache = db
|
|
251
|
-
.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='select_cache'`)
|
|
252
|
-
.all();
|
|
253
|
-
if (hasSelectCache.length === 0) {
|
|
254
|
-
db.exec(`
|
|
255
|
-
CREATE TABLE select_cache (
|
|
256
|
-
component_hash TEXT NOT NULL,
|
|
257
|
-
prompt_hash TEXT NOT NULL,
|
|
258
|
-
cli_version TEXT NOT NULL,
|
|
259
|
-
decision TEXT NOT NULL CHECK (decision IN ('accepted','rejected')),
|
|
260
|
-
reason TEXT,
|
|
261
|
-
created_at TEXT NOT NULL,
|
|
262
|
-
updated_at TEXT NOT NULL,
|
|
263
|
-
PRIMARY KEY (component_hash, prompt_hash, cli_version)
|
|
264
|
-
);
|
|
265
|
-
`);
|
|
266
|
-
}
|
|
267
|
-
const tables = db
|
|
268
|
-
.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='generation_cache'`)
|
|
269
|
-
.all();
|
|
270
|
-
if (tables.length === 0) {
|
|
271
|
-
db.exec(`
|
|
272
|
-
CREATE TABLE generation_cache (
|
|
273
|
-
input_hash TEXT NOT NULL,
|
|
274
|
-
entity_type TEXT NOT NULL CHECK (entity_type IN ('component', 'token_set')),
|
|
275
|
-
entity_id TEXT NOT NULL,
|
|
276
|
-
source_session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
277
|
-
human_edited INTEGER NOT NULL DEFAULT 0 CHECK (human_edited IN (0, 1)),
|
|
278
|
-
created_at TEXT NOT NULL,
|
|
279
|
-
updated_at TEXT NOT NULL,
|
|
280
|
-
PRIMARY KEY (input_hash, entity_type, entity_id)
|
|
281
|
-
);
|
|
282
|
-
CREATE INDEX idx_generation_cache_entity ON generation_cache(entity_type, entity_id);
|
|
283
|
-
CREATE INDEX idx_generation_cache_session ON generation_cache(source_session_id);
|
|
284
|
-
`);
|
|
285
|
-
}
|
|
286
|
-
const genCacheCols = db.prepare(`PRAGMA table_info(generation_cache)`).all();
|
|
287
|
-
const hasPromptHash = genCacheCols.some((c) => c.name === 'prompt_hash');
|
|
288
|
-
const oldPkCols = genCacheCols.filter((c) => c.pk > 0).map((c) => c.name);
|
|
289
|
-
const promptHashInPk = oldPkCols.includes('prompt_hash');
|
|
290
|
-
if (!hasPromptHash) {
|
|
291
|
-
db.exec(`ALTER TABLE generation_cache ADD COLUMN prompt_hash TEXT NOT NULL DEFAULT ''`);
|
|
292
|
-
}
|
|
293
|
-
if (!promptHashInPk) {
|
|
294
|
-
db.exec('BEGIN');
|
|
295
|
-
try {
|
|
296
|
-
db.exec(`
|
|
297
|
-
CREATE TABLE generation_cache__new (
|
|
298
|
-
input_hash TEXT NOT NULL,
|
|
299
|
-
entity_type TEXT NOT NULL CHECK (entity_type IN ('component', 'token_set')),
|
|
300
|
-
entity_id TEXT NOT NULL,
|
|
301
|
-
source_session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
302
|
-
human_edited INTEGER NOT NULL DEFAULT 0 CHECK (human_edited IN (0, 1)),
|
|
303
|
-
created_at TEXT NOT NULL,
|
|
304
|
-
updated_at TEXT NOT NULL,
|
|
305
|
-
prompt_hash TEXT NOT NULL DEFAULT '',
|
|
306
|
-
PRIMARY KEY (input_hash, prompt_hash, entity_type, entity_id)
|
|
307
|
-
);
|
|
308
|
-
INSERT INTO generation_cache__new
|
|
309
|
-
(input_hash, entity_type, entity_id, source_session_id, human_edited, created_at, updated_at, prompt_hash)
|
|
310
|
-
SELECT input_hash, entity_type, entity_id, source_session_id, human_edited, created_at, updated_at, prompt_hash
|
|
311
|
-
FROM generation_cache;
|
|
312
|
-
DROP TABLE generation_cache;
|
|
313
|
-
ALTER TABLE generation_cache__new RENAME TO generation_cache;
|
|
314
|
-
CREATE INDEX IF NOT EXISTS idx_generation_cache_entity ON generation_cache(entity_type, entity_id);
|
|
315
|
-
CREATE INDEX IF NOT EXISTS idx_generation_cache_session ON generation_cache(source_session_id);
|
|
316
|
-
`);
|
|
317
|
-
db.exec('COMMIT');
|
|
318
|
-
}
|
|
319
|
-
catch (e) {
|
|
320
|
-
db.exec('ROLLBACK');
|
|
321
|
-
throw e;
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
const hasCompositionCache = db
|
|
325
|
-
.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='composition_cache'`)
|
|
326
|
-
.all();
|
|
327
|
-
if (hasCompositionCache.length === 0) {
|
|
328
|
-
db.exec(`
|
|
329
|
-
CREATE TABLE composition_cache (
|
|
330
|
-
input_hash TEXT NOT NULL,
|
|
331
|
-
cli_version TEXT NOT NULL,
|
|
332
|
-
agent_output TEXT NOT NULL,
|
|
333
|
-
created_at TEXT NOT NULL,
|
|
334
|
-
updated_at TEXT NOT NULL,
|
|
335
|
-
PRIMARY KEY (input_hash, cli_version)
|
|
336
|
-
);
|
|
337
|
-
`);
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
export function loadComponentReviewMetadata(db, sessionId, componentName) {
|
|
341
|
-
const compRow = db
|
|
342
|
-
.prepare(`SELECT component_id, source, source_path FROM raw_components WHERE session_id = ? AND name = ?`)
|
|
343
|
-
.get(sessionId, componentName);
|
|
344
|
-
if (!compRow)
|
|
345
|
-
return null;
|
|
346
|
-
const propRows = db
|
|
347
|
-
.prepare(`SELECT name, rationale, source_start_line, source_end_line FROM raw_props WHERE session_id = ? AND component_id = ?`)
|
|
348
|
-
.all(sessionId, compRow.component_id);
|
|
349
|
-
const props = {};
|
|
350
|
-
for (const r of propRows) {
|
|
351
|
-
props[r.name] = {
|
|
352
|
-
rationale: r.rationale,
|
|
353
|
-
sourceStartLine: r.source_start_line,
|
|
354
|
-
sourceEndLine: r.source_end_line,
|
|
355
|
-
};
|
|
356
|
-
}
|
|
357
|
-
// raw_components.source historically stores the file path, not the file
|
|
358
|
-
// text. For the source-view panel to render real lines, prefer reading
|
|
359
|
-
// the file from disk via source_path. Fall back to whatever's in `source`
|
|
360
|
-
// (handles in-memory fixtures and tests).
|
|
361
|
-
let componentSource = compRow.source ?? null;
|
|
362
|
-
if (compRow.source_path) {
|
|
363
|
-
try {
|
|
364
|
-
componentSource = readFileSync(compRow.source_path, 'utf8');
|
|
365
|
-
}
|
|
366
|
-
catch {
|
|
367
|
-
// File no longer exists or unreadable — leave fallback.
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
return {
|
|
371
|
-
sourcePath: compRow.source_path,
|
|
372
|
-
componentSource,
|
|
373
|
-
props,
|
|
374
|
-
};
|
|
375
|
-
}
|
|
376
|
-
export function loadComponentRationale(db, sessionId, componentName) {
|
|
377
|
-
const compRow = db
|
|
378
|
-
.prepare(`SELECT component_id, name, description, component_description_rationale, props_rationale, slots_rationale
|
|
379
|
-
FROM raw_components WHERE session_id = ? AND name = ?`)
|
|
380
|
-
.get(sessionId, componentName);
|
|
381
|
-
if (!compRow)
|
|
382
|
-
return null;
|
|
383
|
-
const propRows = db
|
|
384
|
-
.prepare(`SELECT name, cdf_category, category, description, rationale FROM raw_props
|
|
385
|
-
WHERE session_id = ? AND component_id = ? ORDER BY position`)
|
|
386
|
-
.all(sessionId, compRow.component_id);
|
|
387
|
-
const slotRows = db
|
|
388
|
-
.prepare(`SELECT name, description, rationale FROM raw_slots
|
|
389
|
-
WHERE session_id = ? AND component_id = ? ORDER BY position`)
|
|
390
|
-
.all(sessionId, compRow.component_id);
|
|
391
|
-
return {
|
|
392
|
-
name: compRow.name,
|
|
393
|
-
description: compRow.description,
|
|
394
|
-
descriptionRationale: compRow.component_description_rationale,
|
|
395
|
-
propsRationale: compRow.props_rationale,
|
|
396
|
-
slotsRationale: compRow.slots_rationale,
|
|
397
|
-
props: propRows.map((p) => ({
|
|
398
|
-
name: p.name,
|
|
399
|
-
category: p.cdf_category ?? p.category ?? null,
|
|
400
|
-
description: p.description,
|
|
401
|
-
rationale: p.rationale,
|
|
402
|
-
})),
|
|
403
|
-
slots: slotRows.map((s) => ({
|
|
404
|
-
name: s.name,
|
|
405
|
-
description: s.description,
|
|
406
|
-
rationale: s.rationale,
|
|
407
|
-
})),
|
|
408
|
-
};
|
|
409
|
-
}
|
|
410
|
-
export function applyToolCalls(db, sessionId, componentId, componentName, calls, incomingWarnings) {
|
|
411
|
-
const now = new Date().toISOString();
|
|
412
|
-
const warnings = [...incomingWarnings];
|
|
413
|
-
let classified = 0;
|
|
414
|
-
let excluded = 0;
|
|
415
|
-
let slots = 0;
|
|
416
|
-
const updateProp = db.prepare(`UPDATE raw_props SET cdf_type = ?, cdf_category = ?, cdf_token_kind = ?, required = ?, description = ?, rationale = ?
|
|
417
|
-
WHERE session_id = ? AND component_id = ? AND name = ?`);
|
|
418
|
-
const clearProp = db.prepare(`UPDATE raw_props SET cdf_type = 'excluded', cdf_category = NULL, cdf_token_kind = NULL, rationale = ?
|
|
419
|
-
WHERE session_id = ? AND component_id = ? AND name = ?`);
|
|
420
|
-
const deleteAllowedValues = db.prepare(`DELETE FROM raw_prop_allowed_values WHERE session_id = ? AND component_id = ? AND prop_name = ?`);
|
|
421
|
-
const insertAllowedValue = db.prepare(`INSERT OR IGNORE INTO raw_prop_allowed_values (session_id, component_id, prop_name, value, position)
|
|
422
|
-
VALUES (?, ?, ?, ?, ?)`);
|
|
423
|
-
const deleteAllowedComponents = db.prepare(`DELETE FROM raw_slot_allowed_components WHERE session_id = ? AND component_id = ? AND slot_name = ?`);
|
|
424
|
-
const insertAllowedComponent = db.prepare(`INSERT OR IGNORE INTO raw_slot_allowed_components (session_id, component_id, slot_name, allowed_component, position)
|
|
425
|
-
VALUES (?, ?, ?, ?, ?)`);
|
|
426
|
-
const updateSlot = db.prepare(`UPDATE raw_slots SET required = ?, description = ? WHERE session_id = ? AND component_id = ? AND name = ?`);
|
|
427
|
-
db.exec('BEGIN');
|
|
428
|
-
try {
|
|
429
|
-
for (const call of calls) {
|
|
430
|
-
if (call.tool === 'classify_component') {
|
|
431
|
-
if (call.description !== undefined) {
|
|
432
|
-
db.prepare('UPDATE raw_components SET description = ? WHERE session_id = ? AND component_id = ?').run(call.description, sessionId, componentId);
|
|
433
|
-
}
|
|
434
|
-
if (call.rationale) {
|
|
435
|
-
if (call.rationale.description !== undefined) {
|
|
436
|
-
db.prepare('UPDATE raw_components SET component_description_rationale = ? WHERE session_id = ? AND component_id = ?').run(call.rationale.description, sessionId, componentId);
|
|
437
|
-
}
|
|
438
|
-
if (call.rationale.props !== undefined) {
|
|
439
|
-
db.prepare('UPDATE raw_components SET props_rationale = ? WHERE session_id = ? AND component_id = ?').run(call.rationale.props, sessionId, componentId);
|
|
440
|
-
}
|
|
441
|
-
if (call.rationale.slots !== undefined) {
|
|
442
|
-
db.prepare('UPDATE raw_components SET slots_rationale = ? WHERE session_id = ? AND component_id = ?').run(call.rationale.slots, sessionId, componentId);
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
else if (call.tool === 'classify_prop') {
|
|
447
|
-
const changes = updateProp.run(call.cdf_type, call.cdf_category, call.token_kind ?? null, call.required !== undefined ? (call.required ? 1 : 0) : 0, call.description ?? null, call.reason ?? null, sessionId, componentId, call.prop);
|
|
448
|
-
if (changes.changes === 0) {
|
|
449
|
-
warnings.push(`${componentName}: classify_prop '${call.prop}' — prop not found, skipped`);
|
|
450
|
-
continue;
|
|
451
|
-
}
|
|
452
|
-
if (call.values && call.values.length > 0) {
|
|
453
|
-
deleteAllowedValues.run(sessionId, componentId, call.prop);
|
|
454
|
-
call.values.forEach((v, i) => insertAllowedValue.run(sessionId, componentId, call.prop, v, i));
|
|
455
|
-
}
|
|
456
|
-
if (call.default !== undefined) {
|
|
457
|
-
const storedDefault = typeof call.default === 'boolean' ? String(call.default) : call.default;
|
|
458
|
-
db.prepare(`UPDATE raw_props SET default_value = ? WHERE session_id = ? AND component_id = ? AND name = ?`).run(storedDefault, sessionId, componentId, call.prop);
|
|
459
|
-
}
|
|
460
|
-
classified++;
|
|
461
|
-
}
|
|
462
|
-
else if (call.tool === 'exclude_prop') {
|
|
463
|
-
clearProp.run(call.reason || null, sessionId, componentId, call.prop);
|
|
464
|
-
excluded++;
|
|
465
|
-
}
|
|
466
|
-
else if (call.tool === 'classify_slot') {
|
|
467
|
-
const slotRequired = call.required !== undefined ? (call.required ? 1 : 0) : 1;
|
|
468
|
-
const slotChanges = updateSlot.run(slotRequired, call.description ?? null, sessionId, componentId, call.slot);
|
|
469
|
-
if (slotChanges.changes === 0) {
|
|
470
|
-
warnings.push(`${componentName}: classify_slot '${call.slot}' — slot not found, skipped`);
|
|
471
|
-
continue;
|
|
472
|
-
}
|
|
473
|
-
if (call.rationale !== undefined) {
|
|
474
|
-
db.prepare('UPDATE raw_slots SET rationale = ? WHERE session_id = ? AND component_id = ? AND name = ?').run(call.rationale, sessionId, componentId, call.slot);
|
|
475
|
-
}
|
|
476
|
-
if (call.allowed_components !== undefined) {
|
|
477
|
-
deleteAllowedComponents.run(sessionId, componentId, call.slot);
|
|
478
|
-
call.allowed_components.forEach((ac, i) => insertAllowedComponent.run(sessionId, componentId, call.slot, ac, i));
|
|
479
|
-
}
|
|
480
|
-
slots++;
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
db.prepare(`UPDATE raw_components SET status = 'generated', extracted_at = ? WHERE session_id = ? AND component_id = ?`).run(now, sessionId, componentId);
|
|
484
|
-
db.prepare('UPDATE sessions SET updated_at = ? WHERE id = ?').run(now, sessionId);
|
|
485
|
-
db.exec('COMMIT');
|
|
486
|
-
}
|
|
487
|
-
catch (e) {
|
|
488
|
-
db.exec('ROLLBACK');
|
|
489
|
-
throw e;
|
|
490
|
-
}
|
|
491
|
-
return { classified, excluded, slots, warnings };
|
|
492
|
-
}
|
|
493
|
-
export function getOrCreateSession(db, sessionFlag, sessionName, _hints) {
|
|
494
|
-
const now = new Date().toISOString();
|
|
495
|
-
if (sessionFlag === 'new' || sessionFlag === undefined) {
|
|
496
|
-
const id = generateSessionId();
|
|
497
|
-
db.prepare('INSERT INTO sessions (id, name, created_at, updated_at) VALUES (?, ?, ?, ?)').run(id, sessionName ?? null, now, now);
|
|
498
|
-
return { sessionId: id, isNew: true, isResumed: false };
|
|
499
|
-
}
|
|
500
|
-
const existing = db.prepare('SELECT id FROM sessions WHERE id = ?').get(sessionFlag);
|
|
501
|
-
if (!existing) {
|
|
502
|
-
throw new Error(`session '${sessionFlag}' not found. Run 'session list' to see active sessions.`);
|
|
503
|
-
}
|
|
504
|
-
return { sessionId: sessionFlag, isNew: false, isResumed: false };
|
|
505
|
-
}
|
|
506
|
-
export function createStep(db, sessionId, command, inputs) {
|
|
507
|
-
const now = new Date().toISOString();
|
|
508
|
-
db.exec('BEGIN');
|
|
509
|
-
try {
|
|
510
|
-
db.prepare(`UPDATE steps SET status = 'interrupted', completed_at = ?, updated_at = ?
|
|
511
|
-
WHERE session_id = ? AND command = ? AND status = 'pending'`).run(now, now, sessionId, command);
|
|
512
|
-
const result = db
|
|
513
|
-
.prepare(`INSERT INTO steps (session_id, command, status, started_at, inputs, outputs, updated_at)
|
|
514
|
-
VALUES (?, ?, 'pending', ?, ?, '{}', ?)`)
|
|
515
|
-
.run(sessionId, command, now, JSON.stringify(inputs), now);
|
|
516
|
-
db.prepare('UPDATE sessions SET updated_at = ? WHERE id = ?').run(now, sessionId);
|
|
517
|
-
db.exec('COMMIT');
|
|
518
|
-
return Number(result.lastInsertRowid);
|
|
519
|
-
}
|
|
520
|
-
catch (e) {
|
|
521
|
-
db.exec('ROLLBACK');
|
|
522
|
-
throw e;
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
export function updateStep(db, stepId, status, outputs, error) {
|
|
526
|
-
const now = new Date().toISOString();
|
|
527
|
-
db.exec('BEGIN');
|
|
528
|
-
try {
|
|
529
|
-
const step = db.prepare('SELECT session_id FROM steps WHERE id = ?').get(stepId);
|
|
530
|
-
db.prepare(`UPDATE steps SET status = ?, completed_at = ?, outputs = ?, error = ?, updated_at = ? WHERE id = ?`).run(status, now, JSON.stringify(outputs), error ?? null, now, stepId);
|
|
531
|
-
if (step) {
|
|
532
|
-
db.prepare('UPDATE sessions SET updated_at = ? WHERE id = ?').run(now, step.session_id);
|
|
533
|
-
}
|
|
534
|
-
db.exec('COMMIT');
|
|
535
|
-
}
|
|
536
|
-
catch (e) {
|
|
537
|
-
db.exec('ROLLBACK');
|
|
538
|
-
throw e;
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
export function storeRawComponents(db, sessionId, components, options) {
|
|
542
|
-
const now = new Date().toISOString();
|
|
543
|
-
const insertComp = db.prepare(`INSERT INTO raw_components (session_id, component_id, name, source, framework, extracted_at, extraction_confidence, review_reasons, needs_review, source_path)
|
|
544
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
545
|
-
const insertProp = db.prepare(`INSERT INTO raw_props
|
|
546
|
-
(session_id, component_id, name, type, required, category, default_value, description, token_reference, position, source_start_line, source_end_line)
|
|
547
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
548
|
-
const insertAllowedValue = db.prepare(`INSERT INTO raw_prop_allowed_values (session_id, component_id, prop_name, position, value)
|
|
549
|
-
VALUES (?, ?, ?, ?, ?)`);
|
|
550
|
-
const insertSlot = db.prepare(`INSERT INTO raw_slots (session_id, component_id, name, is_default, description, position)
|
|
551
|
-
VALUES (?, ?, ?, ?, ?, ?)`);
|
|
552
|
-
const insertAllowedComponent = db.prepare(`INSERT INTO raw_slot_allowed_components (session_id, component_id, slot_name, position, allowed_component)
|
|
553
|
-
VALUES (?, ?, ?, ?, ?)`);
|
|
554
|
-
db.exec('BEGIN');
|
|
555
|
-
try {
|
|
556
|
-
let cdfSnapshot = [];
|
|
557
|
-
let descSnapshot = [];
|
|
558
|
-
let avSnapshot = [];
|
|
559
|
-
if (options?.preserveCDF) {
|
|
560
|
-
cdfSnapshot = db
|
|
561
|
-
.prepare(`SELECT component_id, name, position, cdf_type, cdf_category, cdf_token_kind
|
|
562
|
-
FROM raw_props WHERE session_id = ? AND cdf_type IS NOT NULL`)
|
|
563
|
-
.all(sessionId);
|
|
564
|
-
descSnapshot = db
|
|
565
|
-
.prepare(`SELECT component_id, description
|
|
566
|
-
FROM raw_components WHERE session_id = ? AND description IS NOT NULL`)
|
|
567
|
-
.all(sessionId);
|
|
568
|
-
if (cdfSnapshot.length > 0) {
|
|
569
|
-
avSnapshot = db
|
|
570
|
-
.prepare(`SELECT component_id, prop_name, position, value
|
|
571
|
-
FROM raw_prop_allowed_values WHERE session_id = ?`)
|
|
572
|
-
.all(sessionId);
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
db.prepare('DELETE FROM raw_components WHERE session_id = ?').run(sessionId);
|
|
576
|
-
for (const comp of components) {
|
|
577
|
-
const componentId = deriveComponentId(comp.name, comp.source);
|
|
578
|
-
insertComp.run(sessionId, componentId, comp.name, comp.source, comp.framework, now, comp.extractionConfidence ?? null, JSON.stringify(comp.reviewReasons ?? []), comp.needsReview ? 1 : 0, comp.sourcePath ?? null);
|
|
579
|
-
for (let i = 0; i < comp.props.length; i++) {
|
|
580
|
-
const prop = comp.props[i];
|
|
581
|
-
insertProp.run(sessionId, componentId, prop.name, prop.type, prop.required ? 1 : 0, prop.category ?? null, prop.defaultValue ?? null, prop.description ?? null, prop.tokenReference ?? null, i, prop.sourceStartLine ?? null, prop.sourceEndLine ?? null);
|
|
582
|
-
if (prop.allowedValues) {
|
|
583
|
-
prop.allowedValues.forEach((v, j) => insertAllowedValue.run(sessionId, componentId, prop.name, j, v));
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
for (let i = 0; i < comp.slots.length; i++) {
|
|
587
|
-
const slot = comp.slots[i];
|
|
588
|
-
insertSlot.run(sessionId, componentId, slot.name, slot.isDefault ? 1 : 0, slot.description ?? null, i);
|
|
589
|
-
if (slot.allowedComponents) {
|
|
590
|
-
slot.allowedComponents.forEach((ac, j) => insertAllowedComponent.run(sessionId, componentId, slot.name, j, ac));
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
if (options?.preserveCDF && cdfSnapshot.length > 0) {
|
|
595
|
-
const updateCDFByName = db.prepare(`UPDATE raw_props SET cdf_type = ?, cdf_category = ?, cdf_token_kind = ?
|
|
596
|
-
WHERE session_id = ? AND component_id = ? AND name = ?`);
|
|
597
|
-
const updateCDFByPosition = db.prepare(`UPDATE raw_props SET cdf_type = ?, cdf_category = ?, cdf_token_kind = ?
|
|
598
|
-
WHERE session_id = ? AND component_id = ? AND position = ? AND cdf_type IS NULL`);
|
|
599
|
-
const restoredPropKeys = new Set();
|
|
600
|
-
const unmatchedSnaps = [];
|
|
601
|
-
for (const snap of cdfSnapshot) {
|
|
602
|
-
const result = updateCDFByName.run(snap.cdf_type, snap.cdf_category, snap.cdf_token_kind, sessionId, snap.component_id, snap.name);
|
|
603
|
-
if (Number(result.changes) > 0) {
|
|
604
|
-
restoredPropKeys.add(`${snap.component_id}::${snap.name}`);
|
|
605
|
-
}
|
|
606
|
-
else {
|
|
607
|
-
unmatchedSnaps.push(snap);
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
for (const snap of unmatchedSnaps) {
|
|
611
|
-
const result = updateCDFByPosition.run(snap.cdf_type, snap.cdf_category, snap.cdf_token_kind, sessionId, snap.component_id, snap.position);
|
|
612
|
-
if (Number(result.changes) > 0) {
|
|
613
|
-
const matched = db
|
|
614
|
-
.prepare(`SELECT name FROM raw_props WHERE session_id = ? AND component_id = ? AND position = ?`)
|
|
615
|
-
.get(sessionId, snap.component_id, snap.position);
|
|
616
|
-
if (matched) {
|
|
617
|
-
restoredPropKeys.add(`${snap.component_id}::${matched.name}`);
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
const updateDesc = db.prepare(`UPDATE raw_components SET description = ? WHERE session_id = ? AND component_id = ?`);
|
|
622
|
-
for (const snap of descSnapshot) {
|
|
623
|
-
updateDesc.run(snap.description, sessionId, snap.component_id);
|
|
624
|
-
}
|
|
625
|
-
const relevantAV = avSnapshot.filter((av) => restoredPropKeys.has(`${av.component_id}::${av.prop_name}`));
|
|
626
|
-
if (relevantAV.length > 0) {
|
|
627
|
-
const deleteAV = db.prepare(`DELETE FROM raw_prop_allowed_values WHERE session_id = ? AND component_id = ? AND prop_name = ?`);
|
|
628
|
-
const insertAV = db.prepare(`INSERT OR IGNORE INTO raw_prop_allowed_values (session_id, component_id, prop_name, position, value)
|
|
629
|
-
VALUES (?, ?, ?, ?, ?)`);
|
|
630
|
-
const deletedKeys = new Set();
|
|
631
|
-
for (const av of relevantAV) {
|
|
632
|
-
const key = `${av.component_id}::${av.prop_name}`;
|
|
633
|
-
if (!deletedKeys.has(key)) {
|
|
634
|
-
deleteAV.run(sessionId, av.component_id, av.prop_name);
|
|
635
|
-
deletedKeys.add(key);
|
|
636
|
-
}
|
|
637
|
-
insertAV.run(sessionId, av.component_id, av.prop_name, av.position, av.value);
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
if (options?.status) {
|
|
642
|
-
db.prepare('UPDATE raw_components SET status = ? WHERE session_id = ?').run(options.status, sessionId);
|
|
643
|
-
}
|
|
644
|
-
db.prepare('UPDATE sessions SET updated_at = ? WHERE id = ?').run(now, sessionId);
|
|
645
|
-
db.exec('COMMIT');
|
|
646
|
-
}
|
|
647
|
-
catch (e) {
|
|
648
|
-
db.exec('ROLLBACK');
|
|
649
|
-
throw e;
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
|
-
export function loadRawComponents(db, sessionId, allowedNames) {
|
|
653
|
-
const all = db
|
|
654
|
-
.prepare('SELECT component_id, name, source, framework, extraction_confidence, review_reasons, needs_review, source_path FROM raw_components WHERE session_id = ? ORDER BY rowid')
|
|
655
|
-
.all(sessionId);
|
|
656
|
-
const components = allowedNames ? all.filter((c) => allowedNames.has(c.name)) : all;
|
|
657
|
-
if (components.length === 0)
|
|
658
|
-
return [];
|
|
659
|
-
const props = db
|
|
660
|
-
.prepare(`SELECT component_id, name, type, required, category, default_value, description, token_reference, position,
|
|
661
|
-
rationale, source_start_line, source_end_line
|
|
662
|
-
FROM raw_props WHERE session_id = ? ORDER BY component_id, position`)
|
|
663
|
-
.all(sessionId);
|
|
664
|
-
const allowedValues = db
|
|
665
|
-
.prepare(`SELECT component_id, prop_name, position, value
|
|
666
|
-
FROM raw_prop_allowed_values WHERE session_id = ? ORDER BY component_id, prop_name, position`)
|
|
667
|
-
.all(sessionId);
|
|
668
|
-
const slots = db
|
|
669
|
-
.prepare(`SELECT component_id, name, is_default, description, position
|
|
670
|
-
FROM raw_slots WHERE session_id = ? ORDER BY component_id, position`)
|
|
671
|
-
.all(sessionId);
|
|
672
|
-
const allowedComponents = db
|
|
673
|
-
.prepare(`SELECT component_id, slot_name, position, allowed_component
|
|
674
|
-
FROM raw_slot_allowed_components WHERE session_id = ? ORDER BY component_id, slot_name, position`)
|
|
675
|
-
.all(sessionId);
|
|
676
|
-
const propsByComponent = groupBy(props, (p) => p.component_id);
|
|
677
|
-
const allowedValuesByProp = groupBy(allowedValues, (av) => `${av.component_id}::${av.prop_name}`);
|
|
678
|
-
const slotsByComponent = groupBy(slots, (s) => s.component_id);
|
|
679
|
-
const allowedComponentsBySlot = groupBy(allowedComponents, (ac) => `${ac.component_id}::${ac.slot_name}`);
|
|
680
|
-
return components.map((c) => ({
|
|
681
|
-
component_id: c.component_id,
|
|
682
|
-
name: c.name,
|
|
683
|
-
source: c.source,
|
|
684
|
-
framework: c.framework,
|
|
685
|
-
extractionConfidence: c.extraction_confidence ?? null,
|
|
686
|
-
reviewReasons: parseReviewReasons(c.review_reasons),
|
|
687
|
-
needsReview: Boolean(c.needs_review),
|
|
688
|
-
sourcePath: c.source_path ?? undefined,
|
|
689
|
-
props: (propsByComponent.get(c.component_id) ?? []).map((p) => {
|
|
690
|
-
const av = allowedValuesByProp.get(`${c.component_id}::${p.name}`);
|
|
691
|
-
const prop = {
|
|
692
|
-
name: p.name,
|
|
693
|
-
type: p.type,
|
|
694
|
-
required: Boolean(p.required),
|
|
695
|
-
};
|
|
696
|
-
if (p.category !== null)
|
|
697
|
-
prop.category = p.category;
|
|
698
|
-
if (p.default_value !== null)
|
|
699
|
-
prop.defaultValue = p.default_value;
|
|
700
|
-
if (p.description !== null)
|
|
701
|
-
prop.description = p.description;
|
|
702
|
-
if (p.token_reference !== null)
|
|
703
|
-
prop.tokenReference = p.token_reference;
|
|
704
|
-
if (av && av.length > 0)
|
|
705
|
-
prop.allowedValues = av.map((v) => v.value);
|
|
706
|
-
if (p.source_start_line !== null)
|
|
707
|
-
prop.sourceStartLine = p.source_start_line;
|
|
708
|
-
if (p.source_end_line !== null)
|
|
709
|
-
prop.sourceEndLine = p.source_end_line;
|
|
710
|
-
return prop;
|
|
711
|
-
}),
|
|
712
|
-
slots: (slotsByComponent.get(c.component_id) ?? []).map((s) => {
|
|
713
|
-
const ac = allowedComponentsBySlot.get(`${c.component_id}::${s.name}`);
|
|
714
|
-
const slot = {
|
|
715
|
-
name: s.name,
|
|
716
|
-
isDefault: Boolean(s.is_default),
|
|
717
|
-
};
|
|
718
|
-
if (s.description !== null)
|
|
719
|
-
slot.description = s.description;
|
|
720
|
-
if (ac && ac.length > 0)
|
|
721
|
-
slot.allowedComponents = ac.map((v) => v.allowed_component);
|
|
722
|
-
return slot;
|
|
723
|
-
}),
|
|
724
|
-
}));
|
|
725
|
-
}
|
|
726
|
-
export function renameEmptySlots(db, sessionId, componentId, componentName, slotCount) {
|
|
727
|
-
const emptySlots = db
|
|
728
|
-
.prepare(`SELECT name, position FROM raw_slots
|
|
729
|
-
WHERE session_id = ? AND component_id = ? AND trim(name) = ''
|
|
730
|
-
ORDER BY position`)
|
|
731
|
-
.all(sessionId, componentId);
|
|
732
|
-
if (emptySlots.length === 0)
|
|
733
|
-
return { renames: [], warnings: [] };
|
|
734
|
-
const renames = [];
|
|
735
|
-
const warnings = [];
|
|
736
|
-
const rename = db.prepare(`UPDATE raw_slots SET name = ? WHERE session_id = ? AND component_id = ? AND name = ? AND position = ?`);
|
|
737
|
-
db.exec('BEGIN');
|
|
738
|
-
try {
|
|
739
|
-
for (const slot of emptySlots) {
|
|
740
|
-
const newName = slotCount === 1 ? 'children' : `slot_${slot.position}`;
|
|
741
|
-
rename.run(newName, sessionId, componentId, slot.name, slot.position);
|
|
742
|
-
renames.push({ oldName: slot.name, newName });
|
|
743
|
-
warnings.push(`${componentName}: slot at position ${slot.position} had empty name — renamed to "${newName}" for classification`);
|
|
744
|
-
}
|
|
745
|
-
db.exec('COMMIT');
|
|
746
|
-
}
|
|
747
|
-
catch (e) {
|
|
748
|
-
db.exec('ROLLBACK');
|
|
749
|
-
throw e;
|
|
750
|
-
}
|
|
751
|
-
return { renames, warnings };
|
|
752
|
-
}
|
|
753
|
-
function parseReviewReasons(raw) {
|
|
754
|
-
try {
|
|
755
|
-
const parsed = JSON.parse(raw ?? '[]');
|
|
756
|
-
return Array.isArray(parsed) ? parsed.filter((reason) => typeof reason === 'string') : [];
|
|
757
|
-
}
|
|
758
|
-
catch {
|
|
759
|
-
// A malformed historical value should not prevent callers from loading.
|
|
760
|
-
return [];
|
|
761
|
-
}
|
|
762
|
-
}
|
|
763
|
-
function groupBy(items, key) {
|
|
764
|
-
const map = new Map();
|
|
765
|
-
for (const item of items) {
|
|
766
|
-
const k = key(item);
|
|
767
|
-
let arr = map.get(k);
|
|
768
|
-
if (!arr) {
|
|
769
|
-
arr = [];
|
|
770
|
-
map.set(k, arr);
|
|
771
|
-
}
|
|
772
|
-
arr.push(item);
|
|
773
|
-
}
|
|
774
|
-
return map;
|
|
775
|
-
}
|
|
776
|
-
function deriveComponentId(name, source) {
|
|
777
|
-
return createHash('sha256').update(`${name}:${source}`).digest('hex').slice(0, 12);
|
|
778
|
-
}
|
|
779
|
-
export function storeCDFComponents(db, sessionId, components) {
|
|
780
|
-
const now = new Date().toISOString();
|
|
781
|
-
const updateComp = db.prepare(`UPDATE raw_components SET status = 'generated', description = ? WHERE session_id = ? AND name = ?`);
|
|
782
|
-
const updateProp = db.prepare(`UPDATE raw_props SET cdf_type = ?, cdf_category = ?, cdf_token_kind = ?, required = ? WHERE session_id = ? AND component_id = ? AND name = ?`);
|
|
783
|
-
const deleteAllowedValues = db.prepare(`DELETE FROM raw_prop_allowed_values WHERE session_id = ? AND component_id = ? AND prop_name = ?`);
|
|
784
|
-
const insertAllowedValue = db.prepare(`INSERT INTO raw_prop_allowed_values (session_id, component_id, prop_name, value, position)
|
|
785
|
-
VALUES (?, ?, ?, ?, ?)`);
|
|
786
|
-
const insertAllowedComponent = db.prepare(`INSERT INTO raw_slot_allowed_components (session_id, component_id, slot_name, allowed_component, position)
|
|
787
|
-
VALUES (?, ?, ?, ?, ?)`);
|
|
788
|
-
const deleteSlots = db.prepare(`DELETE FROM raw_slots WHERE session_id = ? AND component_id = ?`);
|
|
789
|
-
const deleteSlotAllowedComponents = db.prepare(`DELETE FROM raw_slot_allowed_components WHERE session_id = ? AND component_id = ?`);
|
|
790
|
-
const readExistingSlotDefaults = db.prepare(`SELECT name, is_default FROM raw_slots WHERE session_id = ? AND component_id = ?`);
|
|
791
|
-
const insertSlotOnUpdate = db.prepare(`INSERT INTO raw_slots (session_id, component_id, name, is_default, required, description, position)
|
|
792
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)`);
|
|
793
|
-
db.exec('BEGIN');
|
|
794
|
-
try {
|
|
795
|
-
for (const { key, entry } of components) {
|
|
796
|
-
const row = db
|
|
797
|
-
.prepare('SELECT component_id FROM raw_components WHERE session_id = ? AND name = ?')
|
|
798
|
-
.get(sessionId, key);
|
|
799
|
-
if (row) {
|
|
800
|
-
const { component_id: componentId } = row;
|
|
801
|
-
updateComp.run(entry.$description ?? null, sessionId, key);
|
|
802
|
-
for (const [propName, prop] of Object.entries(entry.$properties)) {
|
|
803
|
-
updateProp.run(prop.$type, prop.$category, prop['$token.kind'] ?? null, prop.$required ? 1 : 0, sessionId, componentId, propName);
|
|
804
|
-
if (prop.$values && prop.$values.length > 0) {
|
|
805
|
-
deleteAllowedValues.run(sessionId, componentId, propName);
|
|
806
|
-
prop.$values.forEach((v, i) => insertAllowedValue.run(sessionId, componentId, propName, v, i));
|
|
807
|
-
}
|
|
808
|
-
}
|
|
809
|
-
const existingDefaults = new Map(readExistingSlotDefaults.all(sessionId, componentId).map((r) => [r.name, r.is_default]));
|
|
810
|
-
deleteSlotAllowedComponents.run(sessionId, componentId);
|
|
811
|
-
deleteSlots.run(sessionId, componentId);
|
|
812
|
-
let slotPos = 0;
|
|
813
|
-
for (const [slotName, slot] of Object.entries(entry.$slots ?? {})) {
|
|
814
|
-
const isDefault = existingDefaults.get(slotName) ?? 0;
|
|
815
|
-
insertSlotOnUpdate.run(sessionId, componentId, slotName, isDefault, slot.$required ? 1 : 0, slot.$description ?? null, slotPos++);
|
|
816
|
-
if (slot.$allowedComponents && slot.$allowedComponents.length > 0) {
|
|
817
|
-
slot.$allowedComponents.forEach((ac, i) => insertAllowedComponent.run(sessionId, componentId, slotName, ac, i));
|
|
818
|
-
}
|
|
819
|
-
}
|
|
820
|
-
}
|
|
821
|
-
else {
|
|
822
|
-
const componentId = createHash('sha256').update(`${key}:generated`).digest('hex').slice(0, 12);
|
|
823
|
-
db.prepare(`INSERT INTO raw_components (session_id, component_id, name, source, framework, extracted_at, status, description)
|
|
824
|
-
VALUES (?, ?, ?, '', 'react', ?, 'generated', ?)`).run(sessionId, componentId, key, now, entry.$description ?? null);
|
|
825
|
-
let position = 0;
|
|
826
|
-
for (const [propName, prop] of Object.entries(entry.$properties)) {
|
|
827
|
-
db.prepare(`INSERT OR REPLACE INTO raw_props
|
|
828
|
-
(session_id, component_id, name, type, required, category, default_value, description, token_reference, position, cdf_type, cdf_category, cdf_token_kind)
|
|
829
|
-
VALUES (?, ?, ?, '', ?, NULL, NULL, NULL, NULL, ?, ?, ?, ?)`).run(sessionId, componentId, propName, prop.$required ? 1 : 0, position++, prop.$type, prop.$category, prop['$token.kind'] ?? null);
|
|
830
|
-
if (prop.$values && prop.$values.length > 0) {
|
|
831
|
-
prop.$values.forEach((v, i) => insertAllowedValue.run(sessionId, componentId, propName, v, i));
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
let slotPos = 0;
|
|
835
|
-
for (const [slotName, slot] of Object.entries(entry.$slots ?? {})) {
|
|
836
|
-
db.prepare(`INSERT OR REPLACE INTO raw_slots
|
|
837
|
-
(session_id, component_id, name, is_default, required, description, position)
|
|
838
|
-
VALUES (?, ?, ?, 0, ?, ?, ?)`).run(sessionId, componentId, slotName, slot.$required ? 1 : 0, slot.$description ?? null, slotPos++);
|
|
839
|
-
if (slot.$allowedComponents && slot.$allowedComponents.length > 0) {
|
|
840
|
-
slot.$allowedComponents.forEach((ac, i) => insertAllowedComponent.run(sessionId, componentId, slotName, ac, i));
|
|
841
|
-
}
|
|
842
|
-
}
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
db.prepare('UPDATE sessions SET updated_at = ? WHERE id = ?').run(now, sessionId);
|
|
846
|
-
db.exec('COMMIT');
|
|
847
|
-
}
|
|
848
|
-
catch (e) {
|
|
849
|
-
db.exec('ROLLBACK');
|
|
850
|
-
throw e;
|
|
851
|
-
}
|
|
852
|
-
}
|
|
853
|
-
export function loadCDFComponents(db, sessionId) {
|
|
854
|
-
const components = db
|
|
855
|
-
.prepare(`SELECT component_id, name, description FROM raw_components
|
|
856
|
-
WHERE session_id = ? AND status = 'generated' ORDER BY rowid`)
|
|
857
|
-
.all(sessionId);
|
|
858
|
-
if (components.length === 0)
|
|
859
|
-
return [];
|
|
860
|
-
const props = db
|
|
861
|
-
.prepare(`SELECT component_id, name, required, default_value, description,
|
|
862
|
-
cdf_type, cdf_category, cdf_token_kind, position
|
|
863
|
-
FROM raw_props
|
|
864
|
-
WHERE session_id = ? AND cdf_type IS NOT NULL AND cdf_type != 'excluded'
|
|
865
|
-
ORDER BY component_id, position`)
|
|
866
|
-
.all(sessionId);
|
|
867
|
-
const allowedValues = db
|
|
868
|
-
.prepare(`SELECT component_id, prop_name, value, position
|
|
869
|
-
FROM raw_prop_allowed_values WHERE session_id = ? ORDER BY component_id, prop_name, position`)
|
|
870
|
-
.all(sessionId);
|
|
871
|
-
const slots = db
|
|
872
|
-
.prepare(`SELECT component_id, name, description, required FROM raw_slots WHERE session_id = ? ORDER BY component_id, position`)
|
|
873
|
-
.all(sessionId);
|
|
874
|
-
const allowedComponents = db
|
|
875
|
-
.prepare(`SELECT component_id, slot_name, allowed_component
|
|
876
|
-
FROM raw_slot_allowed_components WHERE session_id = ? ORDER BY component_id, slot_name, position`)
|
|
877
|
-
.all(sessionId);
|
|
878
|
-
const propsByComponent = groupBy(props, (p) => p.component_id);
|
|
879
|
-
const allowedValuesByProp = groupBy(allowedValues, (av) => `${av.component_id}::${av.prop_name}`);
|
|
880
|
-
const slotsByComponent = groupBy(slots, (s) => s.component_id);
|
|
881
|
-
const allowedComponentsBySlot = groupBy(allowedComponents, (ac) => `${ac.component_id}::${ac.slot_name}`);
|
|
882
|
-
return components.map(({ component_id, name, description }) => {
|
|
883
|
-
const compProps = propsByComponent.get(component_id) ?? [];
|
|
884
|
-
const $properties = {};
|
|
885
|
-
for (const p of compProps) {
|
|
886
|
-
if (!p.name.trim())
|
|
887
|
-
continue;
|
|
888
|
-
const av = allowedValuesByProp.get(`${component_id}::${p.name}`);
|
|
889
|
-
const propDef = {
|
|
890
|
-
$type: p.cdf_type,
|
|
891
|
-
$category: p.cdf_category,
|
|
892
|
-
};
|
|
893
|
-
if (p.required)
|
|
894
|
-
propDef.$required = true;
|
|
895
|
-
if (p.default_value !== null) {
|
|
896
|
-
if (p.cdf_type === 'boolean') {
|
|
897
|
-
propDef.$default = p.default_value === 'true';
|
|
898
|
-
}
|
|
899
|
-
else {
|
|
900
|
-
propDef.$default = p.default_value;
|
|
901
|
-
}
|
|
902
|
-
}
|
|
903
|
-
if (p.description !== null)
|
|
904
|
-
propDef.$description = p.description;
|
|
905
|
-
if (av && av.length > 0)
|
|
906
|
-
propDef.$values = av.map((v) => v.value);
|
|
907
|
-
if (p.cdf_token_kind !== null)
|
|
908
|
-
propDef['$token.kind'] = p.cdf_token_kind;
|
|
909
|
-
$properties[p.name] = propDef;
|
|
910
|
-
}
|
|
911
|
-
const compSlots = slotsByComponent.get(component_id) ?? [];
|
|
912
|
-
const $slots = {};
|
|
913
|
-
for (const s of compSlots) {
|
|
914
|
-
if (!s.name.trim())
|
|
915
|
-
continue;
|
|
916
|
-
const ac = allowedComponentsBySlot.get(`${component_id}::${s.name}`);
|
|
917
|
-
const slotDef = {};
|
|
918
|
-
if (s.description !== null)
|
|
919
|
-
slotDef.$description = s.description;
|
|
920
|
-
if (s.required)
|
|
921
|
-
slotDef.$required = true;
|
|
922
|
-
if (ac && ac.length > 0)
|
|
923
|
-
slotDef.$allowedComponents = ac.map((v) => v.allowed_component);
|
|
924
|
-
$slots[s.name] = slotDef;
|
|
925
|
-
}
|
|
926
|
-
const entry = { $type: 'component', $properties };
|
|
927
|
-
if (description !== null)
|
|
928
|
-
entry.$description = description;
|
|
929
|
-
if (Object.keys($slots).length > 0)
|
|
930
|
-
entry.$slots = $slots;
|
|
931
|
-
return { key: name, entry };
|
|
932
|
-
});
|
|
933
|
-
}
|
|
934
|
-
export function loadScopeComponents(db, sessionId) {
|
|
935
|
-
const rows = db
|
|
936
|
-
.prepare(`SELECT name, component_id, status, reject_reason, review_reasons, needs_review FROM raw_components
|
|
937
|
-
WHERE session_id = ? AND status IN ('extracted', 'accepted', 'rejected')
|
|
938
|
-
ORDER BY name`)
|
|
939
|
-
.all(sessionId);
|
|
940
|
-
if (rows.length === 0)
|
|
941
|
-
return [];
|
|
942
|
-
const slotRows = db
|
|
943
|
-
.prepare(`SELECT component_id, name, position
|
|
944
|
-
FROM raw_slots WHERE session_id = ? ORDER BY component_id, position`)
|
|
945
|
-
.all(sessionId);
|
|
946
|
-
const allowedRows = db
|
|
947
|
-
.prepare(`SELECT component_id, slot_name, position, allowed_component
|
|
948
|
-
FROM raw_slot_allowed_components WHERE session_id = ? ORDER BY component_id, slot_name, position`)
|
|
949
|
-
.all(sessionId);
|
|
950
|
-
const slotsByComponent = groupBy(slotRows, (s) => s.component_id);
|
|
951
|
-
const allowedBySlot = groupBy(allowedRows, (a) => `${a.component_id}::${a.slot_name}`);
|
|
952
|
-
return rows.map((r) => {
|
|
953
|
-
const reviewReasons = parseReviewReasons(r.review_reasons);
|
|
954
|
-
const nonAuthorableReason = reviewReasons.find((reason) => reason.startsWith('non-authorable:'));
|
|
955
|
-
return {
|
|
956
|
-
name: r.name,
|
|
957
|
-
componentId: r.component_id,
|
|
958
|
-
aiDecision: r.status === 'accepted' ? 'accepted' : r.status === 'rejected' ? 'rejected' : null,
|
|
959
|
-
aiReason: r.reject_reason ?? nonAuthorableReason?.slice('non-authorable:'.length) ?? null,
|
|
960
|
-
needsReview: Boolean(r.needs_review),
|
|
961
|
-
slots: (slotsByComponent.get(r.component_id) ?? []).map((s) => ({
|
|
962
|
-
name: s.name,
|
|
963
|
-
allowedComponents: (allowedBySlot.get(`${r.component_id}::${s.name}`) ?? []).map((a) => a.allowed_component),
|
|
964
|
-
})),
|
|
965
|
-
};
|
|
966
|
-
});
|
|
967
|
-
}
|
|
968
|
-
export function applyScopeDecisions(db, sessionId, decisions) {
|
|
969
|
-
const now = new Date().toISOString();
|
|
970
|
-
const acceptedSet = new Set(decisions.accepted);
|
|
971
|
-
const accepted = [...acceptedSet];
|
|
972
|
-
const rejected = [...new Set(decisions.rejected)].filter((n) => !acceptedSet.has(n));
|
|
973
|
-
if (rejected.length > 0) {
|
|
974
|
-
const placeholders = rejected.map(() => '?').join(',');
|
|
975
|
-
db.prepare(`UPDATE raw_components SET status = 'rejected' WHERE session_id = ? AND name IN (${placeholders})`).run(sessionId, ...rejected);
|
|
976
|
-
}
|
|
977
|
-
if (accepted.length > 0) {
|
|
978
|
-
const placeholders = accepted.map(() => '?').join(',');
|
|
979
|
-
db.prepare(`UPDATE raw_components SET status = 'generated' WHERE session_id = ? AND name IN (${placeholders})`).run(sessionId, ...accepted);
|
|
980
|
-
}
|
|
981
|
-
db.prepare('UPDATE sessions SET updated_at = ? WHERE id = ?').run(now, sessionId);
|
|
982
|
-
}
|
|
983
|
-
export function storeDTCGTokens(db, sessionId, groups, tokens) {
|
|
984
|
-
const now = new Date().toISOString();
|
|
985
|
-
db.exec('BEGIN');
|
|
986
|
-
try {
|
|
987
|
-
db.prepare('DELETE FROM raw_token_groups WHERE session_id = ?').run(sessionId);
|
|
988
|
-
db.prepare('DELETE FROM raw_tokens WHERE session_id = ?').run(sessionId);
|
|
989
|
-
const insertGroup = db.prepare(`INSERT INTO raw_token_groups (session_id, path, description) VALUES (?, ?, ?)`);
|
|
990
|
-
for (const group of groups) {
|
|
991
|
-
insertGroup.run(sessionId, group.path, group.$description ?? null);
|
|
992
|
-
}
|
|
993
|
-
const insertToken = db.prepare(`INSERT INTO raw_tokens (session_id, path, type, value, description) VALUES (?, ?, ?, ?, ?)`);
|
|
994
|
-
for (const token of tokens) {
|
|
995
|
-
insertToken.run(sessionId, token.path, token.$type, JSON.stringify(token.$value), token.$description ?? null);
|
|
996
|
-
}
|
|
997
|
-
db.prepare('UPDATE sessions SET updated_at = ? WHERE id = ?').run(now, sessionId);
|
|
998
|
-
db.exec('COMMIT');
|
|
999
|
-
}
|
|
1000
|
-
catch (e) {
|
|
1001
|
-
db.exec('ROLLBACK');
|
|
1002
|
-
throw e;
|
|
1003
|
-
}
|
|
1004
|
-
}
|
|
1005
|
-
export function applyTokenToolCalls(db, sessionId, calls, incomingWarnings) {
|
|
1006
|
-
const now = new Date().toISOString();
|
|
1007
|
-
const warnings = [...incomingWarnings];
|
|
1008
|
-
let tokens = 0;
|
|
1009
|
-
let groups = 0;
|
|
1010
|
-
const upsertToken = db.prepare(`INSERT INTO raw_tokens (session_id, path, type, value, description) VALUES (?, ?, ?, ?, ?)
|
|
1011
|
-
ON CONFLICT(session_id, path) DO UPDATE SET type = excluded.type, value = excluded.value, description = excluded.description`);
|
|
1012
|
-
const upsertGroup = db.prepare(`INSERT INTO raw_token_groups (session_id, path, description) VALUES (?, ?, ?)
|
|
1013
|
-
ON CONFLICT(session_id, path) DO UPDATE SET description = excluded.description`);
|
|
1014
|
-
db.exec('BEGIN');
|
|
1015
|
-
try {
|
|
1016
|
-
for (const call of calls) {
|
|
1017
|
-
if (call.tool === 'set_token') {
|
|
1018
|
-
upsertToken.run(sessionId, call.path, call.type, JSON.stringify(call.value), call.description ?? null);
|
|
1019
|
-
tokens++;
|
|
1020
|
-
}
|
|
1021
|
-
else if (call.tool === 'set_group') {
|
|
1022
|
-
upsertGroup.run(sessionId, call.path, call.description ?? null);
|
|
1023
|
-
groups++;
|
|
1024
|
-
}
|
|
1025
|
-
}
|
|
1026
|
-
db.prepare('UPDATE sessions SET updated_at = ? WHERE id = ?').run(now, sessionId);
|
|
1027
|
-
db.exec('COMMIT');
|
|
1028
|
-
}
|
|
1029
|
-
catch (e) {
|
|
1030
|
-
db.exec('ROLLBACK');
|
|
1031
|
-
throw e;
|
|
1032
|
-
}
|
|
1033
|
-
return { tokens, groups, warnings };
|
|
1034
|
-
}
|
|
1035
|
-
export function loadDTCGTokens(db, sessionId) {
|
|
1036
|
-
const groupRows = db
|
|
1037
|
-
.prepare('SELECT path, description FROM raw_token_groups WHERE session_id = ? ORDER BY path')
|
|
1038
|
-
.all(sessionId);
|
|
1039
|
-
const tokenRows = db
|
|
1040
|
-
.prepare('SELECT path, type, value, description FROM raw_tokens WHERE session_id = ? ORDER BY path')
|
|
1041
|
-
.all(sessionId);
|
|
1042
|
-
const groups = groupRows.map((r) => {
|
|
1043
|
-
const prefix = `${r.path}.`;
|
|
1044
|
-
const tokenIds = tokenRows
|
|
1045
|
-
.filter((t) => t.path.startsWith(prefix) && !t.path.slice(prefix.length).includes('.'))
|
|
1046
|
-
.map((t) => t.path);
|
|
1047
|
-
const g = { path: r.path, tokenIds };
|
|
1048
|
-
if (r.description !== null)
|
|
1049
|
-
g.$description = r.description;
|
|
1050
|
-
return g;
|
|
1051
|
-
});
|
|
1052
|
-
const tokens = tokenRows.map((r) => {
|
|
1053
|
-
const t = {
|
|
1054
|
-
path: r.path,
|
|
1055
|
-
$type: r.type,
|
|
1056
|
-
$value: JSON.parse(r.value),
|
|
1057
|
-
};
|
|
1058
|
-
if (r.description !== null)
|
|
1059
|
-
t.$description = r.description;
|
|
1060
|
-
return t;
|
|
1061
|
-
});
|
|
1062
|
-
return { groups, tokens };
|
|
1063
|
-
}
|
|
1064
|
-
export function findLatestSessionForCommand(db, command) {
|
|
1065
|
-
const row = db
|
|
1066
|
-
.prepare(`SELECT s.id FROM sessions s
|
|
1067
|
-
JOIN steps st ON st.session_id = s.id
|
|
1068
|
-
WHERE st.command = ? AND st.status = 'complete'
|
|
1069
|
-
ORDER BY st.started_at DESC, st.id DESC
|
|
1070
|
-
LIMIT 1`)
|
|
1071
|
-
.get(command);
|
|
1072
|
-
return row?.id ?? null;
|
|
1073
|
-
}
|
|
1074
|
-
export function seedCDFFromPriorSession(db, targetSessionId) {
|
|
1075
|
-
const targetComponentIds = db
|
|
1076
|
-
.prepare(`SELECT component_id FROM raw_components WHERE session_id = ?`)
|
|
1077
|
-
.all(targetSessionId);
|
|
1078
|
-
if (targetComponentIds.length === 0)
|
|
1079
|
-
return 0;
|
|
1080
|
-
const placeholders = targetComponentIds.map(() => '?').join(',');
|
|
1081
|
-
const priorRow = db
|
|
1082
|
-
.prepare(`SELECT session_id FROM raw_props
|
|
1083
|
-
WHERE cdf_type IS NOT NULL AND session_id != ?
|
|
1084
|
-
AND component_id IN (${placeholders})
|
|
1085
|
-
ORDER BY rowid DESC LIMIT 1`)
|
|
1086
|
-
.get(targetSessionId, ...targetComponentIds.map((r) => r.component_id));
|
|
1087
|
-
if (!priorRow)
|
|
1088
|
-
return 0;
|
|
1089
|
-
const priorSessionId = priorRow.session_id;
|
|
1090
|
-
const result = db
|
|
1091
|
-
.prepare(`UPDATE raw_props SET
|
|
1092
|
-
cdf_type = (SELECT p2.cdf_type FROM raw_props p2
|
|
1093
|
-
WHERE p2.session_id = ? AND p2.component_id = raw_props.component_id
|
|
1094
|
-
AND p2.name = raw_props.name AND p2.cdf_type IS NOT NULL),
|
|
1095
|
-
cdf_category = (SELECT p2.cdf_category FROM raw_props p2
|
|
1096
|
-
WHERE p2.session_id = ? AND p2.component_id = raw_props.component_id
|
|
1097
|
-
AND p2.name = raw_props.name AND p2.cdf_type IS NOT NULL),
|
|
1098
|
-
cdf_token_kind = (SELECT p2.cdf_token_kind FROM raw_props p2
|
|
1099
|
-
WHERE p2.session_id = ? AND p2.component_id = raw_props.component_id
|
|
1100
|
-
AND p2.name = raw_props.name AND p2.cdf_type IS NOT NULL)
|
|
1101
|
-
WHERE session_id = ? AND cdf_type IS NULL
|
|
1102
|
-
AND EXISTS (SELECT 1 FROM raw_props p2
|
|
1103
|
-
WHERE p2.session_id = ? AND p2.component_id = raw_props.component_id
|
|
1104
|
-
AND p2.name = raw_props.name AND p2.cdf_type IS NOT NULL)`)
|
|
1105
|
-
.run(priorSessionId, priorSessionId, priorSessionId, targetSessionId, priorSessionId);
|
|
1106
|
-
db.prepare(`UPDATE raw_components SET description = (
|
|
1107
|
-
SELECT c2.description FROM raw_components c2
|
|
1108
|
-
WHERE c2.session_id = ? AND c2.component_id = raw_components.component_id
|
|
1109
|
-
AND c2.description IS NOT NULL
|
|
1110
|
-
)
|
|
1111
|
-
WHERE session_id = ? AND description IS NULL
|
|
1112
|
-
AND EXISTS (SELECT 1 FROM raw_components c2
|
|
1113
|
-
WHERE c2.session_id = ? AND c2.component_id = raw_components.component_id
|
|
1114
|
-
AND c2.description IS NOT NULL)`).run(priorSessionId, targetSessionId, priorSessionId);
|
|
1115
|
-
if (result.changes > 0) {
|
|
1116
|
-
db.prepare(`INSERT OR IGNORE INTO raw_prop_allowed_values (session_id, component_id, prop_name, position, value)
|
|
1117
|
-
SELECT ?, av.component_id, av.prop_name, av.position, av.value
|
|
1118
|
-
FROM raw_prop_allowed_values av
|
|
1119
|
-
WHERE av.session_id = ?
|
|
1120
|
-
AND EXISTS (SELECT 1 FROM raw_props p
|
|
1121
|
-
WHERE p.session_id = ? AND p.component_id = av.component_id
|
|
1122
|
-
AND p.name = av.prop_name AND p.cdf_type IS NOT NULL)`).run(targetSessionId, priorSessionId, targetSessionId);
|
|
1123
|
-
}
|
|
1124
|
-
return Number(result.changes);
|
|
1125
|
-
}
|
|
1126
|
-
export function seedCDFFromPreviewResponse(db, sessionId, removedItems) {
|
|
1127
|
-
if (removedItems.length === 0)
|
|
1128
|
-
return 0;
|
|
1129
|
-
let totalSeeded = 0;
|
|
1130
|
-
const updateStmt = db.prepare(`UPDATE raw_props
|
|
1131
|
-
SET cdf_type = ?, cdf_category = ?
|
|
1132
|
-
WHERE session_id = ? AND component_id = ? AND name = ? AND cdf_type IS NULL`);
|
|
1133
|
-
for (const item of removedItems) {
|
|
1134
|
-
if (!item.fullProperties)
|
|
1135
|
-
continue;
|
|
1136
|
-
const localComponent = db
|
|
1137
|
-
.prepare(`SELECT component_id FROM raw_components WHERE session_id = ? AND name = ?`)
|
|
1138
|
-
.get(sessionId, item.name);
|
|
1139
|
-
if (!localComponent)
|
|
1140
|
-
continue;
|
|
1141
|
-
const contentProps = new Set(item.contentProperties);
|
|
1142
|
-
const designProps = new Set(item.designProperties);
|
|
1143
|
-
for (const [propName, propSummary] of Object.entries(item.fullProperties)) {
|
|
1144
|
-
const cdfType = mapServerTypeToCDFType(propSummary.type);
|
|
1145
|
-
if (!cdfType)
|
|
1146
|
-
continue;
|
|
1147
|
-
let cdfCategory = propSummary.category || null;
|
|
1148
|
-
if (!cdfCategory || !['content', 'design', 'state'].includes(cdfCategory)) {
|
|
1149
|
-
if (contentProps.has(propName))
|
|
1150
|
-
cdfCategory = 'content';
|
|
1151
|
-
else if (designProps.has(propName))
|
|
1152
|
-
cdfCategory = 'design';
|
|
1153
|
-
else
|
|
1154
|
-
cdfCategory = 'state';
|
|
1155
|
-
}
|
|
1156
|
-
const result = updateStmt.run(cdfType, cdfCategory, sessionId, localComponent.component_id, propName);
|
|
1157
|
-
totalSeeded += Number(result.changes);
|
|
1158
|
-
}
|
|
1159
|
-
}
|
|
1160
|
-
return totalSeeded;
|
|
1161
|
-
}
|
|
1162
|
-
export function seedDefaultsFromChangedItems(db, sessionId, changedItems) {
|
|
1163
|
-
if (changedItems.length === 0)
|
|
1164
|
-
return 0;
|
|
1165
|
-
let totalSeeded = 0;
|
|
1166
|
-
const updateDefaultStmt = db.prepare(`UPDATE raw_props SET default_value = ?
|
|
1167
|
-
WHERE session_id = ? AND component_id = ? AND name = ? AND default_value IS NULL`);
|
|
1168
|
-
const updateCDFStmt = db.prepare(`UPDATE raw_props SET cdf_type = ?, cdf_category = ?
|
|
1169
|
-
WHERE session_id = ? AND component_id = ? AND name = ? AND cdf_type IS NULL`);
|
|
1170
|
-
for (const { current } of changedItems) {
|
|
1171
|
-
if (!current.fullProperties)
|
|
1172
|
-
continue;
|
|
1173
|
-
const localComponent = db
|
|
1174
|
-
.prepare(`SELECT component_id FROM raw_components WHERE session_id = ? AND name = ?`)
|
|
1175
|
-
.get(sessionId, current.name);
|
|
1176
|
-
if (!localComponent)
|
|
1177
|
-
continue;
|
|
1178
|
-
const contentProps = new Set(current.contentProperties);
|
|
1179
|
-
const designProps = new Set(current.designProperties);
|
|
1180
|
-
for (const [propName, propSummary] of Object.entries(current.fullProperties)) {
|
|
1181
|
-
if (propSummary.default !== undefined && propSummary.default !== null) {
|
|
1182
|
-
const defaultStr = typeof propSummary.default === 'string' ? propSummary.default : JSON.stringify(propSummary.default);
|
|
1183
|
-
const result = updateDefaultStmt.run(defaultStr, sessionId, localComponent.component_id, propName);
|
|
1184
|
-
totalSeeded += Number(result.changes);
|
|
1185
|
-
}
|
|
1186
|
-
const cdfType = mapServerTypeToCDFType(propSummary.type);
|
|
1187
|
-
if (!cdfType)
|
|
1188
|
-
continue;
|
|
1189
|
-
let cdfCategory = propSummary.category || null;
|
|
1190
|
-
if (!cdfCategory || !['content', 'design', 'state'].includes(cdfCategory)) {
|
|
1191
|
-
if (contentProps.has(propName))
|
|
1192
|
-
cdfCategory = 'content';
|
|
1193
|
-
else if (designProps.has(propName))
|
|
1194
|
-
cdfCategory = 'design';
|
|
1195
|
-
else
|
|
1196
|
-
cdfCategory = 'state';
|
|
1197
|
-
}
|
|
1198
|
-
const result = updateCDFStmt.run(cdfType, cdfCategory, sessionId, localComponent.component_id, propName);
|
|
1199
|
-
totalSeeded += Number(result.changes);
|
|
1200
|
-
}
|
|
1201
|
-
}
|
|
1202
|
-
return totalSeeded;
|
|
1203
|
-
}
|
|
1204
|
-
export function backfillUnclassifiedProps(db, sessionId) {
|
|
1205
|
-
const withCategory = db
|
|
1206
|
-
.prepare(`UPDATE raw_props SET cdf_type = 'string', cdf_category = category
|
|
1207
|
-
WHERE session_id = ? AND cdf_type IS NULL AND category IS NOT NULL
|
|
1208
|
-
AND component_id IN (
|
|
1209
|
-
SELECT component_id FROM raw_components WHERE session_id = ? AND status = 'generated'
|
|
1210
|
-
)`)
|
|
1211
|
-
.run(sessionId, sessionId);
|
|
1212
|
-
const withoutCategory = db
|
|
1213
|
-
.prepare(`UPDATE raw_props SET cdf_type = 'string', cdf_category = 'content'
|
|
1214
|
-
WHERE session_id = ? AND cdf_type IS NULL AND category IS NULL
|
|
1215
|
-
AND component_id IN (
|
|
1216
|
-
SELECT component_id FROM raw_components WHERE session_id = ? AND status = 'generated'
|
|
1217
|
-
)`)
|
|
1218
|
-
.run(sessionId, sessionId);
|
|
1219
|
-
return Number(withCategory.changes) + Number(withoutCategory.changes);
|
|
1220
|
-
}
|
|
1221
|
-
function mapServerTypeToCDFType(serverType) {
|
|
1222
|
-
switch (serverType.toLowerCase()) {
|
|
1223
|
-
case 'string':
|
|
1224
|
-
case 'text':
|
|
1225
|
-
return 'string';
|
|
1226
|
-
case 'richtext':
|
|
1227
|
-
return 'richtext';
|
|
1228
|
-
case 'media':
|
|
1229
|
-
return 'media';
|
|
1230
|
-
case 'link':
|
|
1231
|
-
return 'link';
|
|
1232
|
-
case 'enum':
|
|
1233
|
-
case 'symbol':
|
|
1234
|
-
return 'enum';
|
|
1235
|
-
case 'token':
|
|
1236
|
-
return 'token';
|
|
1237
|
-
case 'boolean':
|
|
1238
|
-
return 'boolean';
|
|
1239
|
-
default:
|
|
1240
|
-
return 'string';
|
|
1241
|
-
}
|
|
1242
|
-
}
|
|
1243
|
-
export function computeComponentInputHash(component) {
|
|
1244
|
-
const payload = {
|
|
1245
|
-
framework: component.framework,
|
|
1246
|
-
name: component.name,
|
|
1247
|
-
source: component.source,
|
|
1248
|
-
props: component.props.map((p) => ({
|
|
1249
|
-
name: p.name,
|
|
1250
|
-
type: p.type,
|
|
1251
|
-
})),
|
|
1252
|
-
slots: component.slots.map((s) => ({
|
|
1253
|
-
name: s.name,
|
|
1254
|
-
isDefault: s.isDefault,
|
|
1255
|
-
allowedComponents: [...(s.allowedComponents ?? [])].sort(),
|
|
1256
|
-
})),
|
|
1257
|
-
};
|
|
1258
|
-
return createHash('sha256').update(JSON.stringify(payload)).digest('hex');
|
|
1259
|
-
}
|
|
1260
|
-
export function computeTokenInputHash(rawTokenContent) {
|
|
1261
|
-
return createHash('sha256').update(rawTokenContent.trim()).digest('hex');
|
|
1262
|
-
}
|
|
1263
|
-
export function lookupCache(db, inputHash, entityType, entityId, promptHash = '') {
|
|
1264
|
-
const row = db
|
|
1265
|
-
.prepare(`SELECT input_hash, entity_type, entity_id, source_session_id, human_edited, created_at, updated_at, prompt_hash
|
|
1266
|
-
FROM generation_cache
|
|
1267
|
-
WHERE input_hash = ? AND entity_type = ? AND entity_id = ? AND prompt_hash = ?`)
|
|
1268
|
-
.get(inputHash, entityType, entityId, promptHash);
|
|
1269
|
-
if (!row)
|
|
1270
|
-
return null;
|
|
1271
|
-
return {
|
|
1272
|
-
inputHash: row.input_hash,
|
|
1273
|
-
entityType: row.entity_type,
|
|
1274
|
-
entityId: row.entity_id,
|
|
1275
|
-
sourceSessionId: row.source_session_id,
|
|
1276
|
-
humanEdited: row.human_edited === 1,
|
|
1277
|
-
promptHash: row.prompt_hash,
|
|
1278
|
-
createdAt: row.created_at,
|
|
1279
|
-
updatedAt: row.updated_at,
|
|
1280
|
-
};
|
|
1281
|
-
}
|
|
1282
|
-
export function lookupCacheByEntity(db, entityType, entityId) {
|
|
1283
|
-
const row = db
|
|
1284
|
-
.prepare(`SELECT input_hash, entity_type, entity_id, source_session_id, human_edited, created_at, updated_at, prompt_hash
|
|
1285
|
-
FROM generation_cache
|
|
1286
|
-
WHERE entity_type = ? AND entity_id = ?
|
|
1287
|
-
ORDER BY updated_at DESC LIMIT 1`)
|
|
1288
|
-
.get(entityType, entityId);
|
|
1289
|
-
if (!row)
|
|
1290
|
-
return null;
|
|
1291
|
-
return {
|
|
1292
|
-
inputHash: row.input_hash,
|
|
1293
|
-
entityType: row.entity_type,
|
|
1294
|
-
entityId: row.entity_id,
|
|
1295
|
-
sourceSessionId: row.source_session_id,
|
|
1296
|
-
humanEdited: row.human_edited === 1,
|
|
1297
|
-
promptHash: row.prompt_hash,
|
|
1298
|
-
createdAt: row.created_at,
|
|
1299
|
-
updatedAt: row.updated_at,
|
|
1300
|
-
};
|
|
1301
|
-
}
|
|
1302
|
-
export function storeCache(db, inputHash, entityType, entityId, sourceSessionId, humanEdited, promptHash = '') {
|
|
1303
|
-
const now = new Date().toISOString();
|
|
1304
|
-
db.prepare(`INSERT INTO generation_cache (input_hash, entity_type, entity_id, source_session_id, human_edited, created_at, updated_at, prompt_hash)
|
|
1305
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
1306
|
-
ON CONFLICT(input_hash, prompt_hash, entity_type, entity_id) DO UPDATE SET
|
|
1307
|
-
source_session_id = excluded.source_session_id,
|
|
1308
|
-
human_edited = CASE WHEN generation_cache.human_edited = 1 THEN 1 ELSE excluded.human_edited END,
|
|
1309
|
-
updated_at = excluded.updated_at`).run(inputHash, entityType, entityId, sourceSessionId, humanEdited ? 1 : 0, now, now, promptHash);
|
|
1310
|
-
}
|
|
1311
|
-
export function storeScannedFiles(db, sessionId, filePaths) {
|
|
1312
|
-
db.exec('BEGIN');
|
|
1313
|
-
try {
|
|
1314
|
-
db.prepare('DELETE FROM scanned_files WHERE session_id = ?').run(sessionId);
|
|
1315
|
-
const insert = db.prepare('INSERT INTO scanned_files (session_id, path) VALUES (?, ?)');
|
|
1316
|
-
for (const path of filePaths) {
|
|
1317
|
-
insert.run(sessionId, path);
|
|
1318
|
-
}
|
|
1319
|
-
db.exec('COMMIT');
|
|
1320
|
-
}
|
|
1321
|
-
catch (e) {
|
|
1322
|
-
db.exec('ROLLBACK');
|
|
1323
|
-
throw e;
|
|
1324
|
-
}
|
|
1325
|
-
}
|
|
1326
|
-
export function loadScannedFiles(db, sessionId) {
|
|
1327
|
-
const rows = db.prepare('SELECT path FROM scanned_files WHERE session_id = ? ORDER BY path').all(sessionId);
|
|
1328
|
-
return rows.map((r) => r.path);
|
|
1329
|
-
}
|
|
1330
|
-
export function storeSlotCycles(db, sessionId, cycles) {
|
|
1331
|
-
db.exec('BEGIN');
|
|
1332
|
-
try {
|
|
1333
|
-
db.prepare('DELETE FROM slot_cycles WHERE session_id = ?').run(sessionId);
|
|
1334
|
-
const insert = db.prepare(`INSERT INTO slot_cycles (session_id, cycle_index, path_json, edges_json, suggested_break_json)
|
|
1335
|
-
VALUES (?, ?, ?, ?, ?)`);
|
|
1336
|
-
for (let i = 0; i < cycles.length; i += 1) {
|
|
1337
|
-
const cycle = cycles[i];
|
|
1338
|
-
insert.run(sessionId, i, JSON.stringify(cycle.path), JSON.stringify(cycle.edges), cycle.suggestedBreak ? JSON.stringify(cycle.suggestedBreak) : null);
|
|
1339
|
-
}
|
|
1340
|
-
db.exec('COMMIT');
|
|
1341
|
-
}
|
|
1342
|
-
catch (e) {
|
|
1343
|
-
db.exec('ROLLBACK');
|
|
1344
|
-
throw e;
|
|
1345
|
-
}
|
|
1346
|
-
}
|
|
1347
|
-
export function loadSlotCycles(db, sessionId) {
|
|
1348
|
-
const rows = db
|
|
1349
|
-
.prepare('SELECT cycle_index, path_json, edges_json, suggested_break_json FROM slot_cycles WHERE session_id = ? ORDER BY cycle_index')
|
|
1350
|
-
.all(sessionId);
|
|
1351
|
-
return rows.map((r) => ({
|
|
1352
|
-
path: JSON.parse(r.path_json),
|
|
1353
|
-
edges: JSON.parse(r.edges_json),
|
|
1354
|
-
suggestedBreak: r.suggested_break_json ? JSON.parse(r.suggested_break_json) : null,
|
|
1355
|
-
}));
|
|
1356
|
-
}
|
|
1357
|
-
export function clearSlotCycles(db, sessionId) {
|
|
1358
|
-
db.prepare('DELETE FROM slot_cycles WHERE session_id = ?').run(sessionId);
|
|
1359
|
-
}
|
|
1360
|
-
export function markCacheHumanEdited(db, entityType, entityId) {
|
|
1361
|
-
const now = new Date().toISOString();
|
|
1362
|
-
db.prepare(`UPDATE generation_cache SET human_edited = 1, updated_at = ? WHERE entity_type = ? AND entity_id = ?`).run(now, entityType, entityId);
|
|
1363
|
-
}
|
|
1364
|
-
export function copyComponentFromCache(db, sourceSessionId, targetSessionId, componentId) {
|
|
1365
|
-
const now = new Date().toISOString();
|
|
1366
|
-
db.exec('BEGIN');
|
|
1367
|
-
try {
|
|
1368
|
-
const srcComp = db
|
|
1369
|
-
.prepare(`SELECT description, status FROM raw_components WHERE session_id = ? AND component_id = ?`)
|
|
1370
|
-
.get(sourceSessionId, componentId);
|
|
1371
|
-
if (srcComp) {
|
|
1372
|
-
db.prepare(`UPDATE raw_components SET description = ?, status = ?, extracted_at = ? WHERE session_id = ? AND component_id = ?`).run(srcComp.description, srcComp.status, now, targetSessionId, componentId);
|
|
1373
|
-
}
|
|
1374
|
-
const srcProps = db
|
|
1375
|
-
.prepare(`SELECT name, cdf_type, cdf_category, cdf_token_kind, required, description, default_value
|
|
1376
|
-
FROM raw_props WHERE session_id = ? AND component_id = ?`)
|
|
1377
|
-
.all(sourceSessionId, componentId);
|
|
1378
|
-
for (const p of srcProps) {
|
|
1379
|
-
db.prepare(`UPDATE raw_props SET cdf_type = ?, cdf_category = ?, cdf_token_kind = ?, required = ?, description = ?, default_value = ?
|
|
1380
|
-
WHERE session_id = ? AND component_id = ? AND name = ?`).run(p.cdf_type, p.cdf_category, p.cdf_token_kind, p.required, p.description, p.default_value, targetSessionId, componentId, p.name);
|
|
1381
|
-
}
|
|
1382
|
-
db.prepare(`DELETE FROM raw_prop_allowed_values WHERE session_id = ? AND component_id = ?`).run(targetSessionId, componentId);
|
|
1383
|
-
const srcAV = db
|
|
1384
|
-
.prepare(`SELECT prop_name, position, value FROM raw_prop_allowed_values WHERE session_id = ? AND component_id = ?`)
|
|
1385
|
-
.all(sourceSessionId, componentId);
|
|
1386
|
-
const insertAV = db.prepare(`INSERT INTO raw_prop_allowed_values (session_id, component_id, prop_name, position, value) VALUES (?, ?, ?, ?, ?)`);
|
|
1387
|
-
for (const av of srcAV) {
|
|
1388
|
-
insertAV.run(targetSessionId, componentId, av.prop_name, av.position, av.value);
|
|
1389
|
-
}
|
|
1390
|
-
const srcSlots = db
|
|
1391
|
-
.prepare(`SELECT name, required, description FROM raw_slots WHERE session_id = ? AND component_id = ?`)
|
|
1392
|
-
.all(sourceSessionId, componentId);
|
|
1393
|
-
for (const s of srcSlots) {
|
|
1394
|
-
db.prepare(`UPDATE raw_slots SET required = ?, description = ? WHERE session_id = ? AND component_id = ? AND name = ?`).run(s.required, s.description, targetSessionId, componentId, s.name);
|
|
1395
|
-
}
|
|
1396
|
-
db.prepare(`DELETE FROM raw_slot_allowed_components WHERE session_id = ? AND component_id = ?`).run(targetSessionId, componentId);
|
|
1397
|
-
const srcSAC = db
|
|
1398
|
-
.prepare(`SELECT slot_name, position, allowed_component FROM raw_slot_allowed_components WHERE session_id = ? AND component_id = ?`)
|
|
1399
|
-
.all(sourceSessionId, componentId);
|
|
1400
|
-
const insertSAC = db.prepare(`INSERT INTO raw_slot_allowed_components (session_id, component_id, slot_name, allowed_component, position) VALUES (?, ?, ?, ?, ?)`);
|
|
1401
|
-
for (const sac of srcSAC) {
|
|
1402
|
-
insertSAC.run(targetSessionId, componentId, sac.slot_name, sac.allowed_component, sac.position);
|
|
1403
|
-
}
|
|
1404
|
-
db.prepare('UPDATE sessions SET updated_at = ? WHERE id = ?').run(now, targetSessionId);
|
|
1405
|
-
db.exec('COMMIT');
|
|
1406
|
-
}
|
|
1407
|
-
catch (e) {
|
|
1408
|
-
db.exec('ROLLBACK');
|
|
1409
|
-
throw e;
|
|
1410
|
-
}
|
|
1411
|
-
}
|
|
1412
|
-
export function copyTokensFromCache(db, sourceSessionId, targetSessionId) {
|
|
1413
|
-
const now = new Date().toISOString();
|
|
1414
|
-
db.exec('BEGIN');
|
|
1415
|
-
try {
|
|
1416
|
-
db.prepare('DELETE FROM raw_tokens WHERE session_id = ?').run(targetSessionId);
|
|
1417
|
-
db.prepare('DELETE FROM raw_token_groups WHERE session_id = ?').run(targetSessionId);
|
|
1418
|
-
db.prepare(`INSERT INTO raw_token_groups (session_id, path, description)
|
|
1419
|
-
SELECT ?, path, description FROM raw_token_groups WHERE session_id = ?`).run(targetSessionId, sourceSessionId);
|
|
1420
|
-
db.prepare(`INSERT INTO raw_tokens (session_id, path, type, value, description)
|
|
1421
|
-
SELECT ?, path, type, value, description FROM raw_tokens WHERE session_id = ?`).run(targetSessionId, sourceSessionId);
|
|
1422
|
-
db.prepare('UPDATE sessions SET updated_at = ? WHERE id = ?').run(now, targetSessionId);
|
|
1423
|
-
db.exec('COMMIT');
|
|
1424
|
-
}
|
|
1425
|
-
catch (e) {
|
|
1426
|
-
db.exec('ROLLBACK');
|
|
1427
|
-
throw e;
|
|
1428
|
-
}
|
|
1429
|
-
}
|
|
1430
|
-
let _cliCacheVersionCache = null;
|
|
1431
|
-
export async function getCliCacheVersion() {
|
|
1432
|
-
if (_cliCacheVersionCache)
|
|
1433
|
-
return _cliCacheVersionCache;
|
|
1434
|
-
try {
|
|
1435
|
-
const { hashContent } = await import('./cache-keys.js');
|
|
1436
|
-
const { resolveSkillPath } = await import('@contentful/experience-design-system-generation');
|
|
1437
|
-
const skills = ['components', 'tokens', 'select'];
|
|
1438
|
-
const parts = [];
|
|
1439
|
-
for (const s of skills) {
|
|
1440
|
-
try {
|
|
1441
|
-
const p = resolveSkillPath(s);
|
|
1442
|
-
parts.push(readFileSync(p, 'utf8'));
|
|
1443
|
-
}
|
|
1444
|
-
catch {
|
|
1445
|
-
parts.push(`<missing:${s}>`);
|
|
1446
|
-
}
|
|
1447
|
-
}
|
|
1448
|
-
_cliCacheVersionCache = hashContent(parts.join('\n---\n'));
|
|
1449
|
-
return _cliCacheVersionCache;
|
|
1450
|
-
}
|
|
1451
|
-
catch {
|
|
1452
|
-
_cliCacheVersionCache = 'fallback';
|
|
1453
|
-
return _cliCacheVersionCache;
|
|
1454
|
-
}
|
|
1455
|
-
}
|
|
1456
|
-
export function storeExtractCache(db, filePath, fileHash, cliVersion, components) {
|
|
1457
|
-
const now = new Date().toISOString();
|
|
1458
|
-
const componentsJson = JSON.stringify(components);
|
|
1459
|
-
db.prepare(`INSERT INTO extract_cache (file_path, file_hash, cli_version, created_at, updated_at, components_json)
|
|
1460
|
-
VALUES (?, ?, ?, ?, ?, ?)
|
|
1461
|
-
ON CONFLICT(file_hash, cli_version) DO UPDATE SET
|
|
1462
|
-
file_path = excluded.file_path,
|
|
1463
|
-
updated_at = excluded.updated_at,
|
|
1464
|
-
components_json = excluded.components_json`).run(filePath, fileHash, cliVersion, now, now, componentsJson);
|
|
1465
|
-
}
|
|
1466
|
-
export function lookupExtractCache(db, fileHash, cliVersion) {
|
|
1467
|
-
const row = db
|
|
1468
|
-
.prepare(`SELECT file_path, file_hash, cli_version, created_at, updated_at, components_json
|
|
1469
|
-
FROM extract_cache
|
|
1470
|
-
WHERE file_hash = ? AND cli_version = ?`)
|
|
1471
|
-
.get(fileHash, cliVersion);
|
|
1472
|
-
if (!row)
|
|
1473
|
-
return null;
|
|
1474
|
-
let components = [];
|
|
1475
|
-
try {
|
|
1476
|
-
components = JSON.parse(row.components_json);
|
|
1477
|
-
}
|
|
1478
|
-
catch {
|
|
1479
|
-
return null;
|
|
1480
|
-
}
|
|
1481
|
-
return {
|
|
1482
|
-
filePath: row.file_path,
|
|
1483
|
-
fileHash: row.file_hash,
|
|
1484
|
-
cliVersion: row.cli_version,
|
|
1485
|
-
createdAt: row.created_at,
|
|
1486
|
-
updatedAt: row.updated_at,
|
|
1487
|
-
components,
|
|
1488
|
-
};
|
|
1489
|
-
}
|
|
1490
|
-
/**
|
|
1491
|
-
* Content-addressed cache for the composition resolver's raw agent output
|
|
1492
|
-
* (authored parser source or emitted-edge JSONL). Keyed by a hash of the
|
|
1493
|
-
* candidate files + producer identity (`composition-cache-key.ts`), scoped by
|
|
1494
|
-
* `cli_version` so a prompt/skill change invalidates stale entries. Replaces
|
|
1495
|
-
* the former on-disk `~/.contentful/.../composition-cache` store so all CLI
|
|
1496
|
-
* caches live in the one pipeline DB.
|
|
1497
|
-
*/
|
|
1498
|
-
export function storeCompositionCache(db, inputHash, cliVersion, agentOutput) {
|
|
1499
|
-
const now = new Date().toISOString();
|
|
1500
|
-
db.prepare(`INSERT INTO composition_cache (input_hash, cli_version, agent_output, created_at, updated_at)
|
|
1501
|
-
VALUES (?, ?, ?, ?, ?)
|
|
1502
|
-
ON CONFLICT(input_hash, cli_version) DO UPDATE SET
|
|
1503
|
-
agent_output = excluded.agent_output,
|
|
1504
|
-
updated_at = excluded.updated_at`).run(inputHash, cliVersion, agentOutput, now, now);
|
|
1505
|
-
}
|
|
1506
|
-
export function lookupCompositionCache(db, inputHash, cliVersion) {
|
|
1507
|
-
const row = db
|
|
1508
|
-
.prepare(`SELECT agent_output FROM composition_cache WHERE input_hash = ? AND cli_version = ?`)
|
|
1509
|
-
.get(inputHash, cliVersion);
|
|
1510
|
-
return row ? row.agent_output : null;
|
|
1511
|
-
}
|
|
1512
|
-
export function storeSelectCache(db, componentHash, promptHash, cliVersion, decision, reason) {
|
|
1513
|
-
const now = new Date().toISOString();
|
|
1514
|
-
db.prepare(`INSERT INTO select_cache (component_hash, prompt_hash, cli_version, decision, reason, created_at, updated_at)
|
|
1515
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
1516
|
-
ON CONFLICT(component_hash, prompt_hash, cli_version) DO UPDATE SET
|
|
1517
|
-
decision = excluded.decision,
|
|
1518
|
-
reason = excluded.reason,
|
|
1519
|
-
updated_at = excluded.updated_at`).run(componentHash, promptHash, cliVersion, decision, reason, now, now);
|
|
1520
|
-
}
|
|
1521
|
-
export function lookupSelectCache(db, componentHash, promptHash, cliVersion) {
|
|
1522
|
-
const row = db
|
|
1523
|
-
.prepare(`SELECT component_hash, prompt_hash, cli_version, decision, reason, created_at, updated_at
|
|
1524
|
-
FROM select_cache
|
|
1525
|
-
WHERE component_hash = ? AND prompt_hash = ? AND cli_version = ?`)
|
|
1526
|
-
.get(componentHash, promptHash, cliVersion);
|
|
1527
|
-
if (!row)
|
|
1528
|
-
return null;
|
|
1529
|
-
return {
|
|
1530
|
-
componentHash: row.component_hash,
|
|
1531
|
-
promptHash: row.prompt_hash,
|
|
1532
|
-
cliVersion: row.cli_version,
|
|
1533
|
-
decision: row.decision,
|
|
1534
|
-
reason: row.reason,
|
|
1535
|
-
createdAt: row.created_at,
|
|
1536
|
-
updatedAt: row.updated_at,
|
|
1537
|
-
};
|
|
1538
|
-
}
|