Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
da86193
Route URL imports through Data Liberation CLI
chubes4 Aug 30, 2026
3bd0560
Rebuild Data Liberation bundles
chubes4 Aug 30, 2026
d8e6b58
Sync latest Data Liberation capture fixes
chubes4 Aug 30, 2026
ccd42c3
Compact portable semantic evidence
chubes4 Aug 30, 2026
f88eb77
Keep responsive document switch authoritative
chubes4 Aug 31, 2026
de06301
Sync latest Data Liberation capture
chubes4 Sep 1, 2026
322f7e8
Sync Data Liberation capture geometry and animation recovery
chubes4 Sep 1, 2026
34eebd6
Sync quoted capture URL fix
chubes4 Sep 2, 2026
396d629
Sync apostrophe-safe media rewriting
chubes4 Sep 2, 2026
2087969
Sync complex capture URL preservation
chubes4 Sep 2, 2026
574f9ae
Sync portable srcset encoding
chubes4 Sep 2, 2026
98b6027
Sync bare srcset placeholder cleanup
chubes4 Sep 2, 2026
c4eb8b4
Strip Wix platform banners from captures
chubes4 Sep 2, 2026
e77f100
Strip Wix menu helper chrome
chubes4 Sep 2, 2026
8c1df91
Strip Wix overflow menu helpers
chubes4 Sep 3, 2026
fe64ac2
Sync Data Liberation capture fixes and WordPress destination removal
chubes4 Sep 5, 2026
5275539
Sync capture transaction ordering from Data Liberation
chubes4 Sep 6, 2026
5c9c680
Fix CLI import failure reporting
chubes4 Sep 7, 2026
dca0194
Handle completed import quality failures
chubes4 Sep 7, 2026
ff34b7b
Refresh vendored Data Liberation CLI
chubes4 Sep 7, 2026
60d93e9
Sync Data Liberation at 2cd621cd
chubes4 Sep 7, 2026
0bd1346
Sync clean Data Liberation stack
chubes4 Sep 7, 2026
8da5b13
Rebuild vendored Data Liberation bundles
chubes4 Sep 7, 2026
0f2ffd6
Report failed import summaries
chubes4 Sep 7, 2026
88dcb55
test: preserve sites after failed import quality
chubes4 Sep 7, 2026
c149db7
Sync Data Liberation at b6bde9a1
chubes4 Sep 8, 2026
227773f
Refresh Data Liberation capture guidance
chubes4 Sep 8, 2026
8b3f923
Sync Data Liberation at d9912441
chubes4 Sep 8, 2026
0fe953e
Rebuild vendored Data Liberation bundles
chubes4 Sep 8, 2026
4c284c7
Handle empty Data Liberation driver sets
chubes4 Sep 8, 2026
89e639d
Sync Data Liberation dialog reveal fix
chubes4 Sep 8, 2026
24ebc3a
fix(dla): retain mobile navigation trigger
chubes4 Sep 8, 2026
875d920
fix(dla): preserve fluid gallery heights
chubes4 Sep 8, 2026
68f1c71
build(dla): refresh vendored bundles
chubes4 Sep 8, 2026
76430d7
fix(dla): capture mobile pages as iPhone
chubes4 Sep 8, 2026
eb3862d
sync(dla): update vendored agent
chubes4 Sep 8, 2026
f97c13e
style(dla): format vendored capture source
chubes4 Sep 8, 2026
90bd914
fix(ci): verify canonical Data Liberation bundles
chubes4 Sep 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
6 changes: 5 additions & 1 deletion .buildkite/commands/run-data-liberation-checks.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ bash .buildkite/commands/install-node-dependencies.sh
# show up as untracked/deleted files, not just modified ones — check the
# whole dist/ status, not only the diff.
echo '--- :package: Verify data-liberation plugin bundles are fresh'
# The vendored plugin commits bundles generated from its own lockfile. Studio's
# workspace can hoist compatible but different transitive dependencies, which
# changes otherwise canonical esbuild output.
npm ci --prefix packages/data-liberation-agent --workspaces=false --ignore-scripts --no-audit --no-fund
npm -w data-liberation run build:mcp-bundle
if [[ -n "$(git status --porcelain -- packages/data-liberation-agent/dist)" ]]; then
git status --porcelain -- packages/data-liberation-agent/dist
Expand All @@ -32,7 +36,7 @@ fi
# missing from SKILL_DRIVERS would fail only at plugin-user runtime — catch
# the drift here instead.
echo '--- :mag: Verify skill-invoked drivers have committed bundles'
missing_drivers=$(grep -rhoE 'run\.mjs [a-z0-9_-]+' packages/data-liberation-agent/skills packages/data-liberation-agent/commands 2>/dev/null | awk '{print $2}' | sort -u | while read -r name; do
missing_drivers=$({ grep -rhoE 'run\.mjs [a-z0-9_-]+' packages/data-liberation-agent/skills packages/data-liberation-agent/commands 2>/dev/null || true; } | awk '{print $2}' | sort -u | while read -r name; do
[[ -f "packages/data-liberation-agent/dist/scripts/${name}.mjs" ]] || echo "$name"
done)
if [[ -n "$missing_drivers" ]]; then
Expand Down
127 changes: 120 additions & 7 deletions apps/cli/commands/site/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ import {
} from 'cli/lib/cli-config/core';
import { removeSiteFromConfig } from 'cli/lib/cli-config/sites';
import { connectToDaemon, disconnectFromDaemon, emitCliEvent } from 'cli/lib/daemon-client';
import { liberateWebsite } from 'cli/lib/data-liberation-client';
import {
getAiInstructionsPath,
getWordPressVersionPath,
Expand All @@ -88,7 +89,10 @@ import { getPreferredSiteLanguage } from 'cli/lib/site-language';
import { generateSiteName } from 'cli/lib/site-name';
import { getDefaultSitePath } from 'cli/lib/site-paths';
import { logSiteDetails, openSiteInBrowser, setupCustomDomain } from 'cli/lib/site-utils';
import { keepSqliteIntegrationUpdated } from 'cli/lib/sqlite-integration';
import {
isSqliteIntegrationAvailable,
keepSqliteIntegrationUpdated,
} from 'cli/lib/sqlite-integration';
import { getTracksOrigin, recordTracksEvent, TRACKS_EVENTS } from 'cli/lib/tracks';
import { StatsGroup } from 'cli/lib/types/bump-stats';
import { untildify } from 'cli/lib/utils';
Expand Down Expand Up @@ -517,7 +521,71 @@ function staticSiteImportReceiptError( receipt: Record< string, unknown > | unde
}
const code = ( error as Record< string, unknown > ).code;
const message = ( error as Record< string, unknown > ).message;
return [ code, message ].filter( ( value ) => typeof value === 'string' && value ).join( ': ' );
const detail = [ code, message ]
.filter( ( value ) => typeof value === 'string' && value )
.join( ': ' );
if ( code === 'static_site_importer_quality_failed' ) {
return sprintf(
/* translators: %s: Static Site Importer quality validation detail */
__(
'Static Site Importer materialized a preview but did not accept it. The site and staged request were preserved: %s'
),
detail || __( 'Quality validation failed.' )
);
}
return detail;
}

function staticSiteImportQualityFailure(
receipt: Record< string, unknown > | undefined
): string | undefined {
const response = receipt?.response;
if ( ! response || typeof response !== 'object' || Array.isArray( response ) ) {
return undefined;
}
const result = ( response as Record< string, unknown > ).result;
const importResult =
result && typeof result === 'object' && ! Array.isArray( result )
? ( result as Record< string, unknown > )
: ( response as Record< string, unknown > );
const validation = importResult.import_validation_result;
const summary = importResult.import_report_summary;
const quality =
validation && typeof validation === 'object' && ! Array.isArray( validation )
? validation
: summary && typeof summary === 'object' && ! Array.isArray( summary )
? summary
: undefined;
if ( ! quality ) {
return undefined;
}
const counts = ( quality as Record< string, unknown > ).counts;
const {
status,
quality_pass: qualityPass,
fail_import: failImport,
fallback_count: fallbackCount,
} = quality as Record< string, unknown >;
if ( status !== 'failed' && qualityPass !== false && failImport !== true ) {
return undefined;
}
const fallbackBlocks =
counts && typeof counts === 'object' && ! Array.isArray( counts )
? ( counts as Record< string, unknown > ).fallback_blocks
: fallbackCount;
const detail =
typeof fallbackBlocks === 'number'
? sprintf(
/* translators: %d: number of fallback blocks */
__( 'SSI reported %d fallback blocks.' ),
fallbackBlocks
)
: __( 'SSI rejected the imported content.' );
return sprintf(
/* translators: %s: Static Site Importer validation detail */
__( '%s Review the importer diagnostics and retry.' ),
detail
);
}

async function runStaticSiteImport(
Expand Down Expand Up @@ -583,6 +651,13 @@ async function runStaticSiteImport(
new Error( receiptError || stdout.trim() || __( 'The importer did not return a receipt.' ) )
);
}
const qualityFailure = staticSiteImportQualityFailure( receipt );
if ( qualityFailure ) {
throw new LoggerError(
__( 'Static site import failed quality validation' ),
new Error( qualityFailure )
);
}

const finalizationStartedAt = Date.now();
logger.reportProgress( staticSiteImportProgressMessage( 'finalization', 0 ) );
Expand Down Expand Up @@ -1111,7 +1186,10 @@ function coerceSiteId( value: string ) {
return value;
}

export const registerCommand = ( yargs: StudioArgv ) => {
export const registerCommand = (
yargs: StudioArgv,
dependencies: { liberate?: typeof liberateWebsite } = {}
) => {
return yargs.command( {
command: 'create',
describe: __( 'Create a new site' ),
Expand Down Expand Up @@ -1463,11 +1541,41 @@ export const registerCommand = ( yargs: StudioArgv ) => {
};

try {
const importSource = argv.from;
// Remote URLs are rendered into a local source by Data Liberation before they
// reach SSI; until that path exists here, `resolveStaticSiteImporterSource`
// rejects them. `sourceUrl` still carries provenance for local captures.
let importSource = argv.from;
const sourceUrl = importSource && isUrl( importSource ) ? importSource : undefined;
let liberationOutputDir: string | undefined;
if ( sourceUrl ) {
if ( ! ( await isSqliteIntegrationAvailable() ) ) {
throw new LoggerError(
__(
'Cannot set up WordPress. Bundled SQLite integration files not found. Please reinstall Studio.'
)
);
}
let lastProgressAt = 0;
liberationOutputDir = path.join(
path.dirname( sitePath ),
`${ path.basename( sitePath ) }-source`
);
defaultLogger.reportStart(
LoggerAction.IMPORT_SITE,
__( 'Preparing source website with Data Liberation…' )
);
importSource = await ( dependencies.liberate ?? liberateWebsite )(
sourceUrl,
liberationOutputDir,
{
onProgress: ( message ) => {
const now = Date.now();
if ( now - lastProgressAt >= STATIC_SITE_IMPORT_PROGRESS_INTERVAL_MS ) {
lastProgressAt = now;
defaultLogger.reportProgress( message );
}
},
}
);
defaultLogger.reportSuccess( __( 'Source website prepared' ) );
}

if ( importSource ) {
config.blueprint = buildCreateFromSourceBlueprint(
Expand Down Expand Up @@ -1506,6 +1614,11 @@ export const registerCommand = ( yargs: StudioArgv ) => {

try {
await runCommand( sitePath, config );
if ( sourceUrl && liberationOutputDir ) {
await fs.promises
.rm( liberationOutputDir, { recursive: true, force: true } )
.catch( () => {} );
}
} finally {
const bundlePath = config.blueprint?.staticSiteImport?.bundlePath;
if ( bundlePath ) {
Expand Down
Loading
Loading