@mean-weasel/lineage 0.1.7 → 0.1.9
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/CHANGELOG.md +14 -0
- package/README.md +43 -4
- package/dist/cli/lineage-dev.js +429 -15
- package/dist/cli/lineage-dev.js.map +4 -4
- package/dist/cli/lineage.js +428 -14
- package/dist/cli/lineage.js.map +4 -4
- package/dist/server.js +79 -8
- package/dist/server.js.map +4 -4
- package/dist/web/assets/index-CEb6Sfqx.js +23 -0
- package/dist/web/assets/index-CKMlZT43.css +1 -0
- package/dist/web/index.html +2 -2
- package/package.json +3 -1
- package/dist/web/assets/index-38XlcMya.css +0 -1
- package/dist/web/assets/index-CtsYIUyG.js +0 -23
package/dist/cli/lineage.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli/lineageCli.ts
|
|
4
|
-
import { existsSync as
|
|
4
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "node:fs";
|
|
5
5
|
import { homedir, platform } from "node:os";
|
|
6
|
-
import { dirname as dirname3, join as
|
|
6
|
+
import { dirname as dirname3, join as join7, resolve as resolve5 } from "node:path";
|
|
7
7
|
import { spawn } from "node:child_process";
|
|
8
8
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
9
9
|
|
|
@@ -283,8 +283,8 @@ function isPackageRoot(path) {
|
|
|
283
283
|
const packageJson = join3(path, "package.json");
|
|
284
284
|
if (!existsSync3(packageJson)) return false;
|
|
285
285
|
try {
|
|
286
|
-
const
|
|
287
|
-
return
|
|
286
|
+
const packageInfo2 = JSON.parse(readFileSync2(packageJson, "utf8"));
|
|
287
|
+
return packageInfo2.name === "@mean-weasel/lineage";
|
|
288
288
|
} catch {
|
|
289
289
|
return false;
|
|
290
290
|
}
|
|
@@ -328,6 +328,13 @@ function catalogPath(project = defaultProject) {
|
|
|
328
328
|
function fixtureCatalogPath(project = defaultProject) {
|
|
329
329
|
return join3(repoRoot, "fixtures", cleanProject(project), "assets", "catalog.json");
|
|
330
330
|
}
|
|
331
|
+
function resolvedCatalogPath(project = defaultProject) {
|
|
332
|
+
const path = catalogPath(project);
|
|
333
|
+
if (existsSync3(path)) return path;
|
|
334
|
+
const clean = cleanProject(project);
|
|
335
|
+
if (clean === defaultProject && existsSync3(fixtureCatalogPath(clean))) return fixtureCatalogPath(clean);
|
|
336
|
+
return path;
|
|
337
|
+
}
|
|
331
338
|
function normalizeCatalog(catalog, fallbackProject = defaultProject) {
|
|
332
339
|
const project = cleanProject(catalog.project || catalog.product || fallbackProject);
|
|
333
340
|
const product = catalog.product || project;
|
|
@@ -570,6 +577,10 @@ function listAssets(project = defaultProject, options = {}) {
|
|
|
570
577
|
error
|
|
571
578
|
};
|
|
572
579
|
}
|
|
580
|
+
function validateProject(project = defaultProject) {
|
|
581
|
+
const catalog = loadCatalog(project);
|
|
582
|
+
return { project: catalog.project, product: catalog.product, catalogPath: resolvedCatalogPath(project), default_bucket: catalog.default_bucket, default_region: catalog.default_region, asset_count: catalog.assets.length };
|
|
583
|
+
}
|
|
573
584
|
|
|
574
585
|
// src/server/assetLineageDb.ts
|
|
575
586
|
var require2 = createRequire(import.meta.url);
|
|
@@ -2223,6 +2234,17 @@ function seedLegacyWorkspaces(database, project) {
|
|
|
2223
2234
|
);
|
|
2224
2235
|
}
|
|
2225
2236
|
}
|
|
2237
|
+
function listRows(database, project) {
|
|
2238
|
+
return database.prepare(`
|
|
2239
|
+
select * from lineage_workspaces
|
|
2240
|
+
where project_id = ?
|
|
2241
|
+
order by
|
|
2242
|
+
case status when 'active' then 0 when 'paused' then 1 else 2 end,
|
|
2243
|
+
active_at desc nulls last,
|
|
2244
|
+
updated_at desc,
|
|
2245
|
+
title
|
|
2246
|
+
`).all(project).map(rowToWorkspace);
|
|
2247
|
+
}
|
|
2226
2248
|
function activeWorkspace(database, project) {
|
|
2227
2249
|
const row = database.prepare(`
|
|
2228
2250
|
select * from lineage_workspaces
|
|
@@ -2232,6 +2254,20 @@ function activeWorkspace(database, project) {
|
|
|
2232
2254
|
`).get(project);
|
|
2233
2255
|
return row ? rowToWorkspace(row) : null;
|
|
2234
2256
|
}
|
|
2257
|
+
function listLineageWorkspaces(project) {
|
|
2258
|
+
const database = lineageDb();
|
|
2259
|
+
try {
|
|
2260
|
+
seedLegacyWorkspaces(database, project);
|
|
2261
|
+
return {
|
|
2262
|
+
project,
|
|
2263
|
+
active_workspace: activeWorkspace(database, project),
|
|
2264
|
+
workspaces: listRows(database, project),
|
|
2265
|
+
fetchedAt: nowIso()
|
|
2266
|
+
};
|
|
2267
|
+
} finally {
|
|
2268
|
+
database.close();
|
|
2269
|
+
}
|
|
2270
|
+
}
|
|
2235
2271
|
function activeLineageWorkspaceRoot(project) {
|
|
2236
2272
|
const database = lineageDb();
|
|
2237
2273
|
try {
|
|
@@ -3336,27 +3372,316 @@ function importImageRerollOutput(project = defaultProject, fields) {
|
|
|
3336
3372
|
}
|
|
3337
3373
|
}
|
|
3338
3374
|
|
|
3375
|
+
// src/server/lineageSelectionPacket.ts
|
|
3376
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
3377
|
+
import { existsSync as existsSync5, statSync as statSync4 } from "node:fs";
|
|
3378
|
+
import { isAbsolute, resolve as resolve4 } from "node:path";
|
|
3379
|
+
var LineageSelectionPacketError = class extends Error {
|
|
3380
|
+
constructor(message, warnings = [], errors = []) {
|
|
3381
|
+
super(message);
|
|
3382
|
+
this.warnings = warnings;
|
|
3383
|
+
this.errors = errors;
|
|
3384
|
+
}
|
|
3385
|
+
warnings;
|
|
3386
|
+
errors;
|
|
3387
|
+
};
|
|
3388
|
+
function listAllAssets(project) {
|
|
3389
|
+
const pageSize = 100;
|
|
3390
|
+
const first = listAssets(project, { page: 1, pageSize, source: "all" });
|
|
3391
|
+
const assets = [...first.assets];
|
|
3392
|
+
for (let page = 2; page <= first.pagination.totalPages; page += 1) {
|
|
3393
|
+
assets.push(...listAssets(project, { page, pageSize, source: "all" }).assets);
|
|
3394
|
+
}
|
|
3395
|
+
return assets;
|
|
3396
|
+
}
|
|
3397
|
+
function resolveWorkspace(project, options) {
|
|
3398
|
+
if (options.rootAssetId && options.workspaceId) {
|
|
3399
|
+
throw new LineageSelectionPacketError("Use either --root or --workspace, not both.", [], ["ambiguous_workspace"]);
|
|
3400
|
+
}
|
|
3401
|
+
const snapshot = listLineageWorkspaces(project);
|
|
3402
|
+
const target = options.workspaceId || options.rootAssetId;
|
|
3403
|
+
const workspace = target ? snapshot.workspaces.find((item) => item.id === target || item.root_asset_id === target) : snapshot.active_workspace;
|
|
3404
|
+
if (workspace) return workspace;
|
|
3405
|
+
if (options.rootAssetId) {
|
|
3406
|
+
return {
|
|
3407
|
+
id: lineageWorkspaceId(project, options.rootAssetId),
|
|
3408
|
+
project,
|
|
3409
|
+
root_asset_id: options.rootAssetId,
|
|
3410
|
+
title: `${options.rootAssetId} lineage`,
|
|
3411
|
+
status: "active",
|
|
3412
|
+
created_by: "system",
|
|
3413
|
+
created_at: "",
|
|
3414
|
+
updated_at: ""
|
|
3415
|
+
};
|
|
3416
|
+
}
|
|
3417
|
+
const message = options.workspaceId ? `Unknown lineage workspace: ${options.workspaceId}` : "No active lineage workspace. Pass --root or activate a workspace first.";
|
|
3418
|
+
throw new LineageSelectionPacketError(message, [], [options.workspaceId ? "unknown_workspace" : "missing_active_workspace"]);
|
|
3419
|
+
}
|
|
3420
|
+
function resolveLocalReference(reference) {
|
|
3421
|
+
if (!reference) return void 0;
|
|
3422
|
+
if (isAbsolute(reference)) return reference;
|
|
3423
|
+
const repoRelative = resolve4(repoRoot, reference);
|
|
3424
|
+
if (existsSync5(repoRelative)) return repoRelative;
|
|
3425
|
+
return resolve4(repoRoot, ".asset-scratch", reference);
|
|
3426
|
+
}
|
|
3427
|
+
function fileSize(path) {
|
|
3428
|
+
if (!path || !existsSync5(path)) return void 0;
|
|
3429
|
+
try {
|
|
3430
|
+
return statSync4(path).size;
|
|
3431
|
+
} catch {
|
|
3432
|
+
return void 0;
|
|
3433
|
+
}
|
|
3434
|
+
}
|
|
3435
|
+
function storageState(hasLocal, hasS3) {
|
|
3436
|
+
if (hasLocal && hasS3) return "local_and_s3";
|
|
3437
|
+
if (hasLocal) return "local_only";
|
|
3438
|
+
if (hasS3) return "s3_backed";
|
|
3439
|
+
return "unresolved";
|
|
3440
|
+
}
|
|
3441
|
+
function currentAttemptFor(node) {
|
|
3442
|
+
if (!node.current_attempt) return void 0;
|
|
3443
|
+
return {
|
|
3444
|
+
asset_id: node.current_attempt.asset_id,
|
|
3445
|
+
checksum_sha256: node.current_attempt.checksum_sha256,
|
|
3446
|
+
file_path: node.current_attempt.file_path,
|
|
3447
|
+
generation_job_id: node.current_attempt.generation_job_id,
|
|
3448
|
+
id: node.current_attempt.id,
|
|
3449
|
+
is_current: node.current_attempt.is_current,
|
|
3450
|
+
source: node.current_attempt.source
|
|
3451
|
+
};
|
|
3452
|
+
}
|
|
3453
|
+
function assetForNode(node, catalogAsset, warnings) {
|
|
3454
|
+
const localReference = catalogAsset?.local?.absolute_path || node.current_attempt?.file_path || node.local_path || catalogAsset?.local?.relative_path;
|
|
3455
|
+
const absolutePath = catalogAsset?.local?.absolute_path || resolveLocalReference(localReference);
|
|
3456
|
+
const localExists = absolutePath ? existsSync5(absolutePath) : false;
|
|
3457
|
+
const hasLocalClaim = Boolean(absolutePath || node.local_path || catalogAsset?.local?.relative_path);
|
|
3458
|
+
const s3Key = catalogAsset?.s3?.key || node.s3_key;
|
|
3459
|
+
const hasS3 = Boolean(s3Key);
|
|
3460
|
+
const checksum = catalogAsset?.local?.checksum_sha256 || node.current_attempt?.checksum_sha256 || node.checksum_sha256 || catalogAsset?.s3?.checksum_sha256;
|
|
3461
|
+
const mimeType = catalogAsset?.local?.content_type || catalogAsset?.s3?.content_type;
|
|
3462
|
+
const mediaType = catalogAsset?.content_type || node.media_type;
|
|
3463
|
+
if (hasLocalClaim && !localExists) warnings.push(`Selected asset ${node.asset_id} has a local path but the file is missing: ${absolutePath || node.local_path}`);
|
|
3464
|
+
if (!hasLocalClaim && !hasS3) warnings.push(`Selected asset ${node.asset_id} has neither a local file nor S3 key.`);
|
|
3465
|
+
if (mediaType && mediaType !== "image" && mediaType !== "gif") warnings.push(`Selected asset ${node.asset_id} is ${mediaType}, not an image/gif asset.`);
|
|
3466
|
+
return {
|
|
3467
|
+
asset_id: node.asset_id,
|
|
3468
|
+
campaign: catalogAsset?.campaign || node.campaign,
|
|
3469
|
+
channel: catalogAsset?.channel || node.channel,
|
|
3470
|
+
checksum_sha256: checksum,
|
|
3471
|
+
current_attempt: currentAttemptFor(node),
|
|
3472
|
+
local: {
|
|
3473
|
+
absolute_path: absolutePath,
|
|
3474
|
+
content_type: catalogAsset?.local?.content_type,
|
|
3475
|
+
exists: localExists,
|
|
3476
|
+
relative_path: catalogAsset?.local?.relative_path || node.local_path || node.current_attempt?.file_path,
|
|
3477
|
+
size_bytes: catalogAsset?.local?.size_bytes || fileSize(absolutePath)
|
|
3478
|
+
},
|
|
3479
|
+
media_type: mediaType,
|
|
3480
|
+
mime_type: mimeType,
|
|
3481
|
+
review_notes: node.review_notes,
|
|
3482
|
+
review_state: node.review_state,
|
|
3483
|
+
s3: {
|
|
3484
|
+
bucket: catalogAsset?.s3?.bucket,
|
|
3485
|
+
checksum_sha256: catalogAsset?.s3?.checksum_sha256,
|
|
3486
|
+
content_type: catalogAsset?.s3?.content_type,
|
|
3487
|
+
etag: catalogAsset?.s3?.etag,
|
|
3488
|
+
key: s3Key,
|
|
3489
|
+
region: catalogAsset?.s3?.region,
|
|
3490
|
+
size_bytes: catalogAsset?.s3?.size_bytes,
|
|
3491
|
+
version_id: catalogAsset?.s3?.version_id
|
|
3492
|
+
},
|
|
3493
|
+
selection_note: node.selection_note,
|
|
3494
|
+
source: catalogAsset?.source || node.source,
|
|
3495
|
+
status: catalogAsset?.status || node.status,
|
|
3496
|
+
storage_state: storageState(hasLocalClaim, hasS3),
|
|
3497
|
+
title: catalogAsset?.title || node.title || node.asset_id
|
|
3498
|
+
};
|
|
3499
|
+
}
|
|
3500
|
+
function packetId(packetIdentity) {
|
|
3501
|
+
const digest = createHash3("sha256").update(JSON.stringify(packetIdentity)).digest("hex").slice(0, 24);
|
|
3502
|
+
return `lineage_packet_${digest}`;
|
|
3503
|
+
}
|
|
3504
|
+
function getLineageSelectionPacket(project, options = {}) {
|
|
3505
|
+
const workspace = resolveWorkspace(project, options);
|
|
3506
|
+
const snapshot = getLineageSnapshot(project, workspace.root_asset_id);
|
|
3507
|
+
const catalogSummary = validateProject(project);
|
|
3508
|
+
const catalogAssets = listAllAssets(project);
|
|
3509
|
+
const catalogById = new Map(catalogAssets.map((asset) => [asset.asset_id, asset]));
|
|
3510
|
+
const warnings = [];
|
|
3511
|
+
const errors = [];
|
|
3512
|
+
const nodeById = new Map(snapshot.nodes.map((node) => [node.asset_id, node]));
|
|
3513
|
+
const selectedItems = snapshot.selections.map((selection) => ({
|
|
3514
|
+
asset_id: selection.asset_id,
|
|
3515
|
+
position: selection.position,
|
|
3516
|
+
selected_at: selection.selected_at,
|
|
3517
|
+
selection_note: selection.notes
|
|
3518
|
+
}));
|
|
3519
|
+
if (selectedItems.length === 0) warnings.push(`Lineage workspace ${workspace.id} has no selected assets.`);
|
|
3520
|
+
const assets = selectedItems.flatMap((item) => {
|
|
3521
|
+
const node = nodeById.get(item.asset_id);
|
|
3522
|
+
if (!node) {
|
|
3523
|
+
const message = `Selected asset ${item.asset_id} is not present in the resolved workspace snapshot.`;
|
|
3524
|
+
warnings.push(message);
|
|
3525
|
+
errors.push("selected_asset_outside_workspace");
|
|
3526
|
+
return [];
|
|
3527
|
+
}
|
|
3528
|
+
return [assetForNode(node, catalogById.get(item.asset_id), warnings)];
|
|
3529
|
+
});
|
|
3530
|
+
if (assets.some((asset) => !asset.dimensions)) warnings.push("Image dimensions are unavailable for one or more selected assets.");
|
|
3531
|
+
if (options.strict) {
|
|
3532
|
+
const strictErrors = [
|
|
3533
|
+
...selectedItems.length === 0 ? ["empty_selection"] : [],
|
|
3534
|
+
...assets.filter((asset) => asset.local.absolute_path && !asset.local.exists).map((asset) => `missing_local_file:${asset.asset_id}`),
|
|
3535
|
+
...errors
|
|
3536
|
+
];
|
|
3537
|
+
if (strictErrors.length > 0) {
|
|
3538
|
+
throw new LineageSelectionPacketError(`Lineage selection packet strict mode failed: ${strictErrors.join(", ")}`, warnings, strictErrors);
|
|
3539
|
+
}
|
|
3540
|
+
}
|
|
3541
|
+
const context = {
|
|
3542
|
+
campaign: options.campaign,
|
|
3543
|
+
channel: options.channel,
|
|
3544
|
+
labels: options.labels || [],
|
|
3545
|
+
notes: options.contextNotes
|
|
3546
|
+
};
|
|
3547
|
+
const identity = {
|
|
3548
|
+
assets: assets.map((asset) => ({
|
|
3549
|
+
asset_id: asset.asset_id,
|
|
3550
|
+
checksum_sha256: asset.checksum_sha256,
|
|
3551
|
+
local_path: asset.local.absolute_path,
|
|
3552
|
+
s3_key: asset.s3.key,
|
|
3553
|
+
storage_state: asset.storage_state
|
|
3554
|
+
})),
|
|
3555
|
+
context,
|
|
3556
|
+
project,
|
|
3557
|
+
root_asset_id: workspace.root_asset_id,
|
|
3558
|
+
schema_version: "lineage.selection_packet.v1",
|
|
3559
|
+
selection: selectedItems,
|
|
3560
|
+
workspace_id: workspace.id
|
|
3561
|
+
};
|
|
3562
|
+
return {
|
|
3563
|
+
assets,
|
|
3564
|
+
context,
|
|
3565
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3566
|
+
errors,
|
|
3567
|
+
kind: "lineage.selection_packet",
|
|
3568
|
+
packet_id: packetId(identity),
|
|
3569
|
+
product: catalogSummary.product,
|
|
3570
|
+
project,
|
|
3571
|
+
schema_version: "lineage.selection_packet.v1",
|
|
3572
|
+
selection: {
|
|
3573
|
+
asset_ids: selectedItems.map((item) => item.asset_id),
|
|
3574
|
+
count: selectedItems.length,
|
|
3575
|
+
items: selectedItems,
|
|
3576
|
+
root_asset_id: workspace.root_asset_id
|
|
3577
|
+
},
|
|
3578
|
+
source: {
|
|
3579
|
+
app: "lineage",
|
|
3580
|
+
command: options.command,
|
|
3581
|
+
db_path: options.dbPath || process.env.LINEAGE_DB || lineageDbPath(),
|
|
3582
|
+
package: options.packageVersion
|
|
3583
|
+
},
|
|
3584
|
+
warnings: [...new Set(warnings)],
|
|
3585
|
+
workspace: {
|
|
3586
|
+
active_at: workspace.active_at,
|
|
3587
|
+
id: workspace.id,
|
|
3588
|
+
notes: workspace.notes,
|
|
3589
|
+
root_asset_id: workspace.root_asset_id,
|
|
3590
|
+
status: workspace.status,
|
|
3591
|
+
title: workspace.title
|
|
3592
|
+
}
|
|
3593
|
+
};
|
|
3594
|
+
}
|
|
3595
|
+
|
|
3596
|
+
// src/server/runtimeInfo.ts
|
|
3597
|
+
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
3598
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
3599
|
+
import { existsSync as existsSync6, readFileSync as readFileSync3, statSync as statSync5 } from "node:fs";
|
|
3600
|
+
import { join as join6 } from "node:path";
|
|
3601
|
+
var require3 = createRequire2(import.meta.url);
|
|
3602
|
+
function packageInfo() {
|
|
3603
|
+
try {
|
|
3604
|
+
const info = JSON.parse(readFileSync3(join6(repoRoot, "package.json"), "utf8"));
|
|
3605
|
+
return { name: info.name || "@mean-weasel/lineage", version: info.version || "0.0.0" };
|
|
3606
|
+
} catch {
|
|
3607
|
+
return { name: "@mean-weasel/lineage", version: "0.0.0" };
|
|
3608
|
+
}
|
|
3609
|
+
}
|
|
3610
|
+
function normalizeRuntimeChannel(value) {
|
|
3611
|
+
if (value === "stable" || value === "preview" || value === "dev") return value;
|
|
3612
|
+
if (value === "production") return "stable";
|
|
3613
|
+
if (value === "next") return "preview";
|
|
3614
|
+
if (value === "development") return "dev";
|
|
3615
|
+
return process.env.NODE_ENV === "production" ? "stable" : "dev";
|
|
3616
|
+
}
|
|
3617
|
+
function gitSha() {
|
|
3618
|
+
const envSha = process.env.LINEAGE_GIT_SHA || process.env.GITHUB_SHA;
|
|
3619
|
+
if (envSha) return envSha.slice(0, 40);
|
|
3620
|
+
if (!existsSync6(join6(repoRoot, ".git"))) return void 0;
|
|
3621
|
+
const result = spawnSync2("git", ["rev-parse", "--short=12", "HEAD"], { cwd: repoRoot, encoding: "utf8" });
|
|
3622
|
+
return result.status === 0 ? result.stdout.trim() || void 0 : void 0;
|
|
3623
|
+
}
|
|
3624
|
+
function tableExists(database, table) {
|
|
3625
|
+
return Boolean(database.prepare("select name from sqlite_master where type = 'table' and name = ?").get(table));
|
|
3626
|
+
}
|
|
3627
|
+
function tableCount(database, table) {
|
|
3628
|
+
if (!tableExists(database, table)) return void 0;
|
|
3629
|
+
const row = database.prepare(`select count(*) count from ${table}`).get();
|
|
3630
|
+
return typeof row?.count === "number" ? row.count : void 0;
|
|
3631
|
+
}
|
|
3632
|
+
function getLineageRuntimeInfo(options = {}) {
|
|
3633
|
+
const info = packageInfo();
|
|
3634
|
+
const dbPath = options.dbPath || lineageDbPath();
|
|
3635
|
+
const databaseInfo = { exists: existsSync6(dbPath), path: dbPath };
|
|
3636
|
+
if (databaseInfo.exists) {
|
|
3637
|
+
try {
|
|
3638
|
+
const stat = statSync5(dbPath);
|
|
3639
|
+
databaseInfo.modified_at = stat.mtime.toISOString();
|
|
3640
|
+
databaseInfo.size_bytes = stat.size;
|
|
3641
|
+
const { DatabaseSync } = require3("node:sqlite");
|
|
3642
|
+
const database = new DatabaseSync(dbPath, { readOnly: true });
|
|
3643
|
+
try {
|
|
3644
|
+
databaseInfo.projects = tableCount(database, "projects");
|
|
3645
|
+
databaseInfo.workspaces = tableCount(database, "lineage_workspaces");
|
|
3646
|
+
} finally {
|
|
3647
|
+
database.close();
|
|
3648
|
+
}
|
|
3649
|
+
} catch (error) {
|
|
3650
|
+
databaseInfo.error = error instanceof Error ? error.message : String(error);
|
|
3651
|
+
}
|
|
3652
|
+
}
|
|
3653
|
+
return {
|
|
3654
|
+
channel: normalizeRuntimeChannel(options.channel || process.env.LINEAGE_CHANNEL || process.env.LINEAGE_RELEASE_CHANNEL),
|
|
3655
|
+
database: databaseInfo,
|
|
3656
|
+
fetchedAt: nowIso(),
|
|
3657
|
+
git_sha: gitSha(),
|
|
3658
|
+
node_env: process.env.NODE_ENV,
|
|
3659
|
+
package_name: info.name,
|
|
3660
|
+
version: info.version
|
|
3661
|
+
};
|
|
3662
|
+
}
|
|
3663
|
+
|
|
3339
3664
|
// src/cli/lineageCli.ts
|
|
3340
3665
|
var signalExitCodes = {
|
|
3341
3666
|
SIGINT: 130,
|
|
3342
3667
|
SIGTERM: 143
|
|
3343
3668
|
};
|
|
3344
3669
|
function packageRoot() {
|
|
3345
|
-
return
|
|
3670
|
+
return resolve5(dirname3(fileURLToPath2(import.meta.url)), "..", "..");
|
|
3346
3671
|
}
|
|
3347
3672
|
function packageVersion() {
|
|
3348
3673
|
try {
|
|
3349
|
-
const
|
|
3350
|
-
return
|
|
3674
|
+
const packageInfo2 = JSON.parse(readFileSync4(join7(packageRoot(), "package.json"), "utf8"));
|
|
3675
|
+
return packageInfo2.version || "0.0.0";
|
|
3351
3676
|
} catch {
|
|
3352
3677
|
return "0.0.0";
|
|
3353
3678
|
}
|
|
3354
3679
|
}
|
|
3355
3680
|
function dataRoot(displayName) {
|
|
3356
3681
|
if (process.env.LINEAGE_HOME) return process.env.LINEAGE_HOME;
|
|
3357
|
-
if (platform() === "darwin") return
|
|
3358
|
-
if (platform() === "win32") return
|
|
3359
|
-
return
|
|
3682
|
+
if (platform() === "darwin") return join7(homedir(), "Library", "Application Support", displayName);
|
|
3683
|
+
if (platform() === "win32") return join7(process.env.APPDATA || join7(homedir(), "AppData", "Roaming"), displayName);
|
|
3684
|
+
return join7(process.env.XDG_DATA_HOME || join7(homedir(), ".local", "share"), displayName.toLowerCase().replace(/\s+/g, "-"));
|
|
3360
3685
|
}
|
|
3361
3686
|
function readOption(args, name) {
|
|
3362
3687
|
const prefix = `${name}=`;
|
|
@@ -3366,6 +3691,19 @@ function readOption(args, name) {
|
|
|
3366
3691
|
if (index >= 0) return args[index + 1];
|
|
3367
3692
|
return void 0;
|
|
3368
3693
|
}
|
|
3694
|
+
function readOptions(args, name) {
|
|
3695
|
+
const values = [];
|
|
3696
|
+
const prefix = `${name}=`;
|
|
3697
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
3698
|
+
const arg = args[index];
|
|
3699
|
+
if (arg.startsWith(prefix)) values.push(arg.slice(prefix.length));
|
|
3700
|
+
else if (arg === name && args[index + 1] && !args[index + 1].startsWith("--")) {
|
|
3701
|
+
values.push(args[index + 1]);
|
|
3702
|
+
index += 1;
|
|
3703
|
+
}
|
|
3704
|
+
}
|
|
3705
|
+
return values;
|
|
3706
|
+
}
|
|
3369
3707
|
function resolveStartOptions(config, args) {
|
|
3370
3708
|
const runtimeDir = dataRoot(config.displayName);
|
|
3371
3709
|
const rawPort = readOption(args, "--port") || process.env.PORT || String(config.defaultPort);
|
|
@@ -3374,7 +3712,7 @@ function resolveStartOptions(config, args) {
|
|
|
3374
3712
|
throw new Error(`Invalid port: ${rawPort}`);
|
|
3375
3713
|
}
|
|
3376
3714
|
return {
|
|
3377
|
-
dbPath: readOption(args, "--db") || process.env.LINEAGE_DB ||
|
|
3715
|
+
dbPath: readOption(args, "--db") || process.env.LINEAGE_DB || join7(runtimeDir, `${config.binName}.sqlite`),
|
|
3378
3716
|
host: readOption(args, "--host") || process.env.HOST || config.defaultHost,
|
|
3379
3717
|
json: args.includes("--json"),
|
|
3380
3718
|
open: args.includes("--open"),
|
|
@@ -3389,6 +3727,7 @@ Usage:
|
|
|
3389
3727
|
${config.binName} next [--project <project>] [--root <asset-id>] [--db <path>] [--json]
|
|
3390
3728
|
${config.binName} brief [--project <project>] [--root <asset-id>] [--db <path>] [--json]
|
|
3391
3729
|
${config.binName} inspect --asset-id <asset-id> [--project <project>] [--db <path>] [--json]
|
|
3730
|
+
${config.binName} selection packet [--project <project>] [--workspace <id-or-root>|--root <asset-id>] [--channel <channel>] [--campaign <campaign>] [--context-notes <text>] [--label <label>] [--out <path>] [--strict] [--db <path>] [--json]
|
|
3392
3731
|
${config.binName} link-child --root <asset-id> --child <asset-id> [--project <project>] [--claim-token <claim-id.secret>] [--confirm-write] [--db <path>] [--json]
|
|
3393
3732
|
${config.binName} reroll list --root <asset-id> [--project <project>] [--db <path>] [--json]
|
|
3394
3733
|
${config.binName} reroll mark --root <asset-id> --target <asset-id> [--notes <text>] [--requested-by agent|human|system] [--project <project>] [--confirm-write] [--db <path>] [--json]
|
|
@@ -3403,6 +3742,7 @@ Usage:
|
|
|
3403
3742
|
${config.binName} tasks cancel --task <task-id> [--confirm-write] [--override] [--project <project>] [--db <path>] [--json]
|
|
3404
3743
|
${config.binName} tasks override --task <task-id> --reason <text> [--instructions <text>] [--project <project>] [--db <path>] [--json]
|
|
3405
3744
|
${config.binName} tasks instructions --task <task-id> --instructions <text> [--project <project>] [--db <path>] [--json]
|
|
3745
|
+
${config.binName} db info [--db <path>] [--json]
|
|
3406
3746
|
${config.binName} agent claim --project <project> --scope <scope> --target <target-id> --agent-name <name> [--channel <channel>] [--ttl 20m] [--json]
|
|
3407
3747
|
${config.binName} agent graph --root <asset-id> [--project <project>] [--db <path>] [--json]
|
|
3408
3748
|
${config.binName} agent status [--project <project>] [--json]
|
|
@@ -3464,6 +3804,31 @@ function runLineageDataCommand(command, args) {
|
|
|
3464
3804
|
if (!options.assetId) throw new Error("lineage inspect requires --asset-id");
|
|
3465
3805
|
return getLineageSnapshot(options.project, options.assetId);
|
|
3466
3806
|
}
|
|
3807
|
+
if (command === "selection") {
|
|
3808
|
+
const subcommand = positionalArgs(args)[0] || "";
|
|
3809
|
+
if (subcommand !== "packet") throw new Error(`Unknown selection command: ${subcommand}`);
|
|
3810
|
+
const labels = readOptions(args, "--label").flatMap((label) => label.split(",")).map((label) => label.trim()).filter(Boolean);
|
|
3811
|
+
const packet = getLineageSelectionPacket(options.project, {
|
|
3812
|
+
campaign: readOption(args, "--campaign"),
|
|
3813
|
+
channel: readOption(args, "--channel"),
|
|
3814
|
+
command: "lineage selection packet",
|
|
3815
|
+
contextNotes: readOption(args, "--context-notes") || readOption(args, "--notes"),
|
|
3816
|
+
dbPath: options.dbPath,
|
|
3817
|
+
labels,
|
|
3818
|
+
packageVersion: packageVersion(),
|
|
3819
|
+
rootAssetId: options.rootAssetId,
|
|
3820
|
+
strict: args.includes("--strict"),
|
|
3821
|
+
workspaceId: readOption(args, "--workspace") || readOption(args, "--workspace-id")
|
|
3822
|
+
});
|
|
3823
|
+
const out = readOption(args, "--out");
|
|
3824
|
+
if (out) {
|
|
3825
|
+
const outPath = resolve5(out);
|
|
3826
|
+
mkdirSync4(dirname3(outPath), { recursive: true });
|
|
3827
|
+
writeFileSync2(outPath, `${JSON.stringify(packet, null, 2)}
|
|
3828
|
+
`);
|
|
3829
|
+
}
|
|
3830
|
+
return packet;
|
|
3831
|
+
}
|
|
3467
3832
|
if (command === "link-child") {
|
|
3468
3833
|
if (!options.childAssetId) throw new Error("lineage link-child requires --child");
|
|
3469
3834
|
return linkSelectedLineageChild(options.project, {
|
|
@@ -3613,6 +3978,13 @@ function printDataResult(command, result, json) {
|
|
|
3613
3978
|
console.log(`Active: ${snapshot.active_asset_id}`);
|
|
3614
3979
|
return;
|
|
3615
3980
|
}
|
|
3981
|
+
if (command === "selection" && result && typeof result === "object" && "packet_id" in result) {
|
|
3982
|
+
const packet = result;
|
|
3983
|
+
console.log(`${packet.packet_id}: ${packet.selection?.count || packet.assets?.length || 0} selected asset(s)`);
|
|
3984
|
+
if (packet.workspace?.root_asset_id) console.log(`Root: ${packet.workspace.root_asset_id}`);
|
|
3985
|
+
for (const warning of packet.warnings || []) console.log(`Warning: ${warning}`);
|
|
3986
|
+
return;
|
|
3987
|
+
}
|
|
3616
3988
|
if (command === "link-child" && result && typeof result === "object") {
|
|
3617
3989
|
const link = result;
|
|
3618
3990
|
console.log(link.message || `${link.dryRun ? "Dry run: " : ""}Link ${link.edge?.child_asset_id || "child"} from ${link.edge?.parent_asset_id || "parent"}`);
|
|
@@ -3655,6 +4027,34 @@ function printDataResult(command, result, json) {
|
|
|
3655
4027
|
}
|
|
3656
4028
|
console.log(String(result));
|
|
3657
4029
|
}
|
|
4030
|
+
function runLineageDbCommand(config, command, args) {
|
|
4031
|
+
const dbPath = readOption(args, "--db");
|
|
4032
|
+
if (dbPath) process.env.LINEAGE_DB = dbPath;
|
|
4033
|
+
if (command === "info") return getLineageRuntimeInfo({ channel: config.channel, dbPath });
|
|
4034
|
+
throw new Error(`Unknown db command: ${command}`);
|
|
4035
|
+
}
|
|
4036
|
+
function printDbResult(command, result, json) {
|
|
4037
|
+
if (json) {
|
|
4038
|
+
console.log(JSON.stringify(result, null, 2));
|
|
4039
|
+
return;
|
|
4040
|
+
}
|
|
4041
|
+
if (command === "info" && result && typeof result === "object" && "database" in result) {
|
|
4042
|
+
const runtime = result;
|
|
4043
|
+
console.log(`Channel: ${runtime.channel}`);
|
|
4044
|
+
console.log(`Version: ${runtime.version}`);
|
|
4045
|
+
if (runtime.git_sha) console.log(`Git: ${runtime.git_sha}`);
|
|
4046
|
+
if (runtime.node_env) console.log(`Node env: ${runtime.node_env}`);
|
|
4047
|
+
console.log(`SQLite: ${runtime.database.path}`);
|
|
4048
|
+
console.log(`Exists: ${runtime.database.exists ? "yes" : "no"}`);
|
|
4049
|
+
if (runtime.database.size_bytes !== void 0) console.log(`Size: ${runtime.database.size_bytes} bytes`);
|
|
4050
|
+
if (runtime.database.modified_at) console.log(`Modified: ${runtime.database.modified_at}`);
|
|
4051
|
+
if (runtime.database.projects !== void 0) console.log(`Projects: ${runtime.database.projects}`);
|
|
4052
|
+
if (runtime.database.workspaces !== void 0) console.log(`Workspaces: ${runtime.database.workspaces}`);
|
|
4053
|
+
if (runtime.database.error) console.log(`Warning: ${runtime.database.error}`);
|
|
4054
|
+
return;
|
|
4055
|
+
}
|
|
4056
|
+
console.log(String(result));
|
|
4057
|
+
}
|
|
3658
4058
|
function runLineageAgentCommand(command, args) {
|
|
3659
4059
|
const dbPath = readOption(args, "--db");
|
|
3660
4060
|
if (dbPath) process.env.LINEAGE_DB = dbPath;
|
|
@@ -3781,8 +4181,8 @@ function start(config, args) {
|
|
|
3781
4181
|
else console.error(`${config.binName}: ${message}`);
|
|
3782
4182
|
process.exit(1);
|
|
3783
4183
|
}
|
|
3784
|
-
const serverPath =
|
|
3785
|
-
if (!
|
|
4184
|
+
const serverPath = join7(packageRoot(), "dist", "server.js");
|
|
4185
|
+
if (!existsSync7(serverPath)) {
|
|
3786
4186
|
const message = `Missing bundled server at ${serverPath}. Run npm run build before using ${config.binName} start from a source checkout.`;
|
|
3787
4187
|
if (options.json) console.error(JSON.stringify({ ok: false, error: message }, null, 2));
|
|
3788
4188
|
else console.error(`${config.binName}: ${message}`);
|
|
@@ -3836,7 +4236,7 @@ function runLineageCli(config, args = process.argv.slice(2)) {
|
|
|
3836
4236
|
start(config, normalizedArgs.slice(1));
|
|
3837
4237
|
return;
|
|
3838
4238
|
}
|
|
3839
|
-
if (command === "next" || command === "brief" || command === "inspect" || command === "link-child" || command === "reroll" || command === "tasks") {
|
|
4239
|
+
if (command === "next" || command === "brief" || command === "inspect" || command === "selection" || command === "link-child" || command === "reroll" || command === "tasks") {
|
|
3840
4240
|
const commandArgs = normalizedArgs.slice(1);
|
|
3841
4241
|
const json2 = commandArgs.includes("--json");
|
|
3842
4242
|
try {
|
|
@@ -3851,6 +4251,20 @@ function runLineageCli(config, args = process.argv.slice(2)) {
|
|
|
3851
4251
|
}
|
|
3852
4252
|
process.exit(0);
|
|
3853
4253
|
}
|
|
4254
|
+
if (command === "db") {
|
|
4255
|
+
const commandArgs = normalizedArgs.slice(2);
|
|
4256
|
+
const dbCommand = normalizedArgs[1] || "";
|
|
4257
|
+
const json2 = commandArgs.includes("--json");
|
|
4258
|
+
try {
|
|
4259
|
+
printDbResult(dbCommand, runLineageDbCommand(config, dbCommand, commandArgs), json2);
|
|
4260
|
+
} catch (error) {
|
|
4261
|
+
const message2 = error instanceof Error ? error.message : String(error);
|
|
4262
|
+
if (json2) console.error(JSON.stringify({ ok: false, command: `db ${dbCommand}`, error: message2 }, null, 2));
|
|
4263
|
+
else console.error(`${config.binName}: ${message2}`);
|
|
4264
|
+
process.exit(1);
|
|
4265
|
+
}
|
|
4266
|
+
process.exit(0);
|
|
4267
|
+
}
|
|
3854
4268
|
if (command === "agent") {
|
|
3855
4269
|
const commandArgs = normalizedArgs.slice(2);
|
|
3856
4270
|
const agentCommand = normalizedArgs[1] || "";
|