@signaliz/cli 1.0.11 → 1.0.13
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/bin.js +344 -5
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -1071,12 +1071,15 @@ Campaign Builder:
|
|
|
1071
1071
|
Merge a private-safe strategy template
|
|
1072
1072
|
--builtins a,b lead_generation,local_leads,email_finding,email_verification,signals
|
|
1073
1073
|
--custom-tool ROUTE BYO route: provider:layer:tool[:mcp]
|
|
1074
|
+
campaign-agent doctor Inspect built-in lanes, BYO routes, Memory, Brain, and approvals
|
|
1074
1075
|
campaign-agent proof Prove memory, plan, approvals, and build dry-run without spend
|
|
1075
1076
|
campaign-agent commit-plan
|
|
1076
1077
|
Dry-run or write a reviewed GTM campaign object
|
|
1077
1078
|
campaign-agent dry-run Execute build_campaign with dry_run=true
|
|
1078
1079
|
campaign-agent build-approved
|
|
1079
1080
|
Launch only after explicit approval metadata
|
|
1081
|
+
campaign-agent review <id>
|
|
1082
|
+
Review rows, artifacts, and delivery gates
|
|
1080
1083
|
campaign-agent status <id>
|
|
1081
1084
|
Inspect a campaign-agent build
|
|
1082
1085
|
campaign-agent rows <id>
|
|
@@ -2789,10 +2792,12 @@ var CAMPAIGN_AGENT_HELP = `signaliz campaign-agent <command> [options]
|
|
|
2789
2792
|
Strategy-template campaign builder:
|
|
2790
2793
|
init Emit reusable CampaignBuilderAgentRequest JSON
|
|
2791
2794
|
plan Compose an approval-gated strategy-template MCP flow
|
|
2795
|
+
doctor Inspect built-in lanes, BYO routes, Memory, Brain, and approvals
|
|
2792
2796
|
proof Prove memory, plan, approvals, and build dry-run without spend
|
|
2793
2797
|
commit-plan Dry-run or write a reviewed GTM campaign object
|
|
2794
2798
|
dry-run Execute build_campaign with dry_run=true
|
|
2795
2799
|
build-approved Launch only after explicit approval metadata
|
|
2800
|
+
review Review status, rows, artifacts, and delivery gates
|
|
2796
2801
|
status Inspect a campaign-agent build
|
|
2797
2802
|
rows Retrieve reviewable campaign-agent rows
|
|
2798
2803
|
artifacts List generated artifacts
|
|
@@ -2800,12 +2805,14 @@ Strategy-template campaign builder:
|
|
|
2800
2805
|
|
|
2801
2806
|
Examples:
|
|
2802
2807
|
signaliz campaign-agent init --strategy-template non-medical-home-care --target-count 250 --use-local-leads --json > campaign-request.json
|
|
2808
|
+
signaliz campaign-agent doctor --request-file campaign-request.json --json
|
|
2803
2809
|
signaliz campaign-agent plan --goal "Build a strategy-template campaign for my ICP" --strategy-template non-medical-home-care --target-count 500 --builtins lead_generation,email_finding,email_verification,signals
|
|
2804
2810
|
signaliz campaign-agent proof --goal "Build a strategy-template campaign for my ICP" --strategy-template non-medical-home-care --target-count 100 --json
|
|
2805
2811
|
signaliz campaign-agent plan --request-file campaign-builder-agent-request.json --target-count 250 --json
|
|
2806
2812
|
signaliz campaign-agent dry-run --goal "Build a local services campaign" --target-count 250 --use-local-leads --json
|
|
2807
2813
|
signaliz campaign-agent build-approved --goal "Build a strategy-template campaign" --confirm-launch --approve-all --approved-by operator@example.com --spend-limit 500
|
|
2808
2814
|
signaliz campaign-agent build-approved --request-file campaign-builder-agent-request.json --confirm-launch --approve-all --approved-by operator@example.com --spend-limit 500 --wait --approve-delivery --delivery-destination-type json
|
|
2815
|
+
signaliz campaign-agent review <campaign_build_id> --limit 100 --json
|
|
2809
2816
|
signaliz campaign-agent status <campaign_build_id> --json
|
|
2810
2817
|
signaliz campaign-agent rows <campaign_build_id> --limit 100 --json
|
|
2811
2818
|
|
|
@@ -2860,11 +2867,11 @@ async function campaignAgent(sub, rest) {
|
|
|
2860
2867
|
process.stdout.write(CAMPAIGN_AGENT_HELP);
|
|
2861
2868
|
return;
|
|
2862
2869
|
}
|
|
2863
|
-
if (["status", "rows", "artifacts", "approve", "approve-delivery"].includes(sub)) {
|
|
2870
|
+
if (["review", "status", "rows", "artifacts", "approve", "approve-delivery"].includes(sub)) {
|
|
2864
2871
|
await campaignAgentReview(sub, rest);
|
|
2865
2872
|
return;
|
|
2866
2873
|
}
|
|
2867
|
-
if (!["init", "template", "request-template", "new", "plan", "proof", "smoke", "commit-plan", "dry-run", "build-approved", "launch-approved"].includes(sub)) {
|
|
2874
|
+
if (!["init", "template", "request-template", "new", "plan", "doctor", "readiness", "preflight", "proof", "smoke", "commit-plan", "dry-run", "build-approved", "launch-approved"].includes(sub)) {
|
|
2868
2875
|
process.stdout.write(CAMPAIGN_AGENT_HELP);
|
|
2869
2876
|
return;
|
|
2870
2877
|
}
|
|
@@ -2882,7 +2889,8 @@ async function campaignAgent(sub, rest) {
|
|
|
2882
2889
|
return;
|
|
2883
2890
|
}
|
|
2884
2891
|
const requestFromFile = campaignAgentRequestFileFromFlags(flags);
|
|
2885
|
-
const
|
|
2892
|
+
const isDoctor = sub === "doctor" || sub === "readiness" || sub === "preflight";
|
|
2893
|
+
const goal = commandStringFlag(flags, "goal", "brief") || promptArg(rest) || campaignAgentStringValue(requestFromFile?.goal) || (isDoctor ? "Build a strategy-template campaign for the target ICP." : void 0);
|
|
2886
2894
|
if (!goal) die(`campaign-agent ${sub} requires --goal or a request file with goal`);
|
|
2887
2895
|
const isApprovedBuild = sub === "build-approved" || sub === "launch-approved";
|
|
2888
2896
|
const isCommitPlan = sub === "commit-plan";
|
|
@@ -2901,13 +2909,19 @@ async function campaignAgent(sub, rest) {
|
|
|
2901
2909
|
requestFromFile,
|
|
2902
2910
|
campaignAgentRequestFromFlags(goal, flags, { includeDefaults: !requestFromFile })
|
|
2903
2911
|
);
|
|
2904
|
-
const
|
|
2912
|
+
const planOptions = {
|
|
2905
2913
|
discoverCapabilities: !commandBooleanFlag(flags, "skip-discovery"),
|
|
2906
2914
|
scopeCampaign: !commandBooleanFlag(flags, "skip-scope"),
|
|
2907
2915
|
includeStrategyMemoryStatus: !commandBooleanFlag(flags, "no-strategy-memory"),
|
|
2908
2916
|
includeKernelPlan: !commandBooleanFlag(flags, "no-kernel-plan"),
|
|
2909
2917
|
includeDeliveryRisk: !commandBooleanFlag(flags, "no-delivery-risk")
|
|
2910
|
-
}
|
|
2918
|
+
};
|
|
2919
|
+
if (isDoctor) {
|
|
2920
|
+
const readiness = await agent.readiness?.(request, planOptions) || createCampaignAgentReadiness(await agent.createPlan(request, planOptions));
|
|
2921
|
+
if (!readiness.summary?.ready) process.exitCode = 1;
|
|
2922
|
+
return output(readiness, () => printCampaignAgentReadiness(readiness));
|
|
2923
|
+
}
|
|
2924
|
+
const plan = await agent.createPlan(request, planOptions);
|
|
2911
2925
|
if (sub === "proof" || sub === "smoke") {
|
|
2912
2926
|
const result = await agent.dryRunPlan(plan, {
|
|
2913
2927
|
idempotencyKey: commandStringFlag(flags, "idempotency-key")
|
|
@@ -2974,6 +2988,24 @@ async function campaignAgentReview(sub, rest) {
|
|
|
2974
2988
|
const sdk = getSdk();
|
|
2975
2989
|
const agent = sdk.campaignBuilderAgent;
|
|
2976
2990
|
const campaigns = sdk.campaigns || {};
|
|
2991
|
+
if (sub === "review") {
|
|
2992
|
+
const rowOptions = {
|
|
2993
|
+
limit: commandNumberFlag(flags, "limit") ?? 100,
|
|
2994
|
+
cursor: commandStringFlag(flags, "cursor"),
|
|
2995
|
+
segment: commandStringFlag(flags, "segment"),
|
|
2996
|
+
rowStatus: commandStringFlag(flags, "row-status"),
|
|
2997
|
+
qualified: commandBooleanFlag(flags, "qualified") ? true : commandBooleanFlag(flags, "disqualified") ? false : void 0
|
|
2998
|
+
};
|
|
2999
|
+
const review = await agent.reviewBuild?.(buildId, rowOptions) || await campaignAgentBuildReviewFromCampaignApis(agent, campaigns, buildId, rowOptions);
|
|
3000
|
+
const operatorReview = {
|
|
3001
|
+
...review,
|
|
3002
|
+
rows: {
|
|
3003
|
+
...review.rows,
|
|
3004
|
+
rows: formatCampaignRowsForOperator(review.rows?.rows || [])
|
|
3005
|
+
}
|
|
3006
|
+
};
|
|
3007
|
+
return output(operatorReview, () => printCampaignAgentBuildReview(operatorReview));
|
|
3008
|
+
}
|
|
2977
3009
|
if (sub === "status") {
|
|
2978
3010
|
const status = await (agent.getBuildStatus?.bind(agent) || campaigns.status?.bind(campaigns) || campaigns.getCampaignBuildStatus?.bind(campaigns))?.(buildId);
|
|
2979
3011
|
if (!status) die("campaign-agent status requires a compatible @signaliz/sdk campaign status API");
|
|
@@ -3025,6 +3057,13 @@ async function campaignAgentReview(sub, rest) {
|
|
|
3025
3057
|
if (result.message) console.log(result.message);
|
|
3026
3058
|
});
|
|
3027
3059
|
}
|
|
3060
|
+
async function campaignAgentBuildReviewFromCampaignApis(agent, campaigns, buildId, rowOptions) {
|
|
3061
|
+
const status = await (agent.getBuildStatus?.bind(agent) || campaigns.status?.bind(campaigns) || campaigns.getCampaignBuildStatus?.bind(campaigns))?.(buildId);
|
|
3062
|
+
const rows = await (agent.getBuildRows?.bind(agent) || campaigns.rows?.bind(campaigns) || campaigns.getCampaignBuildRows?.bind(campaigns))?.(buildId, rowOptions);
|
|
3063
|
+
const artifacts = await (agent.listBuildArtifacts?.bind(agent) || campaigns.artifacts?.bind(campaigns) || campaigns.listCampaignBuildArtifacts?.bind(campaigns))?.(buildId);
|
|
3064
|
+
if (!status || !rows || !artifacts) die("campaign-agent review requires compatible @signaliz/sdk build status, rows, and artifacts APIs");
|
|
3065
|
+
return createCampaignAgentBuildReview(status, rows, artifacts);
|
|
3066
|
+
}
|
|
3028
3067
|
function campaignAgentRequestTemplateFromFlags(flags, rest) {
|
|
3029
3068
|
const goal = commandStringFlag(flags, "goal", "brief") || promptArg(rest) || "Build a strategy-template campaign for mature operators in the target ICP.";
|
|
3030
3069
|
const request = campaignAgentRequestFromFlags(goal, flags, { includeDefaults: true });
|
|
@@ -3305,6 +3344,172 @@ function createCampaignAgentApproval(plan, input) {
|
|
|
3305
3344
|
approvedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3306
3345
|
};
|
|
3307
3346
|
}
|
|
3347
|
+
function createCampaignAgentReadiness(plan) {
|
|
3348
|
+
const lanes = firstArray(plan?.routes).map((route) => {
|
|
3349
|
+
const builtIn = campaignAgentBuiltInForRoute(route);
|
|
3350
|
+
const customRoute = route?.provider !== "signaliz" && route?.provider !== "csv" && !builtIn;
|
|
3351
|
+
const prepareStep = firstArray(plan?.mcpFlow).find(
|
|
3352
|
+
(step) => step?.tool === "gtm_provider_recipe_prepare" && record(step?.arguments).provider_id === route?.provider
|
|
3353
|
+
);
|
|
3354
|
+
const activateStep = firstArray(plan?.mcpFlow).find(
|
|
3355
|
+
(step) => step?.tool === "gtm_provider_route_activate" && record(step?.arguments).provider_id === route?.provider
|
|
3356
|
+
);
|
|
3357
|
+
const activationArgs = record(activateStep?.arguments);
|
|
3358
|
+
const readOnlySetup = !customRoute || prepareStep?.readOnly === true && activateStep?.readOnly === true && activationArgs.dry_run === true && activationArgs.confirm === false;
|
|
3359
|
+
const blockers2 = [
|
|
3360
|
+
customRoute && !prepareStep ? `${route?.label || route?.provider} is missing provider recipe setup.` : void 0,
|
|
3361
|
+
customRoute && !activateStep ? `${route?.label || route?.provider} is missing provider route dry-run setup.` : void 0,
|
|
3362
|
+
customRoute && !readOnlySetup ? `${route?.label || route?.provider} route setup must stay read-only before approval.` : void 0,
|
|
3363
|
+
route?.mode === "required" && !route?.toolName && customRoute ? `${route?.label || route?.provider} is required but has no tool name.` : void 0
|
|
3364
|
+
].filter(Boolean);
|
|
3365
|
+
return {
|
|
3366
|
+
id: route?.id || `${route?.provider}-${route?.layer}`,
|
|
3367
|
+
label: route?.label || campaignAgentReadinessLaneLabel(route, builtIn),
|
|
3368
|
+
layer: route?.layer,
|
|
3369
|
+
gtmLayers: firstArray(route?.gtmLayers).length ? route.gtmLayers : [route?.gtmLayer].filter(Boolean),
|
|
3370
|
+
provider: route?.provider,
|
|
3371
|
+
toolName: route?.toolName,
|
|
3372
|
+
mode: route?.mode || "if_available",
|
|
3373
|
+
builtIn,
|
|
3374
|
+
approvalRequired: route?.approvalRequired === true || firstArray(plan?.approvals).some((approval) => approval?.routeId === route?.id),
|
|
3375
|
+
customRoute,
|
|
3376
|
+
readOnlySetup,
|
|
3377
|
+
ready: blockers2.length === 0,
|
|
3378
|
+
blockers: blockers2,
|
|
3379
|
+
nextAction: customRoute ? `Review ${route?.provider} route setup before launch approval.` : void 0
|
|
3380
|
+
};
|
|
3381
|
+
});
|
|
3382
|
+
const builtInLanes = lanes.filter((lane) => lane.builtIn);
|
|
3383
|
+
const customRoutes = lanes.filter((lane) => lane.customRoute);
|
|
3384
|
+
const strategyMemory = record(plan?.strategyMemoryStatus);
|
|
3385
|
+
const deliveryRisk = record(record(plan?.buildRequest).deliveryRisk);
|
|
3386
|
+
const risk = record(deliveryRisk.risk);
|
|
3387
|
+
const riskLevel = String(risk.risk_level || risk.riskLevel || "").toLowerCase();
|
|
3388
|
+
const riskBlockers = firstArray(deliveryRisk.blockers);
|
|
3389
|
+
const gates = [
|
|
3390
|
+
{
|
|
3391
|
+
id: "campaign_plan_created",
|
|
3392
|
+
label: "Campaign plan created",
|
|
3393
|
+
passed: Boolean(plan?.planId && firstArray(plan?.mcpFlow).length),
|
|
3394
|
+
detail: plan?.planId ? `Plan ${plan.planId} has ${firstArray(plan?.mcpFlow).length} MCP step(s).` : "No campaign-builder plan was created."
|
|
3395
|
+
},
|
|
3396
|
+
{
|
|
3397
|
+
id: "built_in_lanes_ready",
|
|
3398
|
+
label: "Built-in lanes ready",
|
|
3399
|
+
passed: builtInLanes.length > 0 && builtInLanes.every((lane) => lane.ready),
|
|
3400
|
+
detail: `${builtInLanes.filter((lane) => lane.ready).length}/${builtInLanes.length} Signaliz built-in lane(s) are present.`
|
|
3401
|
+
},
|
|
3402
|
+
{
|
|
3403
|
+
id: "strategy_memory_ready",
|
|
3404
|
+
label: "Strategy memory ready",
|
|
3405
|
+
passed: !plan?.strategyMemoryStatus || strategyMemory.ready !== false,
|
|
3406
|
+
detail: plan?.strategyMemoryStatus ? "Strategy memory readiness was checked." : "Strategy memory readiness was not requested."
|
|
3407
|
+
},
|
|
3408
|
+
{
|
|
3409
|
+
id: "kernel_plan_attached",
|
|
3410
|
+
label: "Kernel plan attached",
|
|
3411
|
+
passed: Object.keys(record(plan?.kernelPlan)).length > 0,
|
|
3412
|
+
detail: Object.keys(record(plan?.kernelPlan)).length > 0 ? "Kernel planner evidence is attached." : "Kernel planner evidence is not attached."
|
|
3413
|
+
},
|
|
3414
|
+
{
|
|
3415
|
+
id: "delivery_risk_clear",
|
|
3416
|
+
label: "Delivery risk clear",
|
|
3417
|
+
passed: Object.keys(deliveryRisk).length === 0 || riskBlockers.length === 0 && !["high", "critical", "blocked"].includes(riskLevel),
|
|
3418
|
+
detail: Object.keys(deliveryRisk).length === 0 ? "Delivery risk preflight was not requested." : `Delivery risk is ${riskLevel || "available"} with ${riskBlockers.length} blocker(s).`
|
|
3419
|
+
},
|
|
3420
|
+
{
|
|
3421
|
+
id: "custom_routes_prepared",
|
|
3422
|
+
label: "Custom routes prepared",
|
|
3423
|
+
passed: customRoutes.every((lane) => lane.ready),
|
|
3424
|
+
detail: customRoutes.length === 0 ? "No BYO routes were requested." : `${customRoutes.filter((lane) => lane.ready).length}/${customRoutes.length} BYO route(s) have read-only setup steps.`
|
|
3425
|
+
},
|
|
3426
|
+
{
|
|
3427
|
+
id: "approval_gates_present",
|
|
3428
|
+
label: "Approval gates present",
|
|
3429
|
+
passed: firstArray(plan?.approvals).length > 0,
|
|
3430
|
+
detail: firstArray(plan?.approvals).length > 0 ? `${firstArray(plan?.approvals).length} approval gate(s) are attached.` : "No approval gates are attached."
|
|
3431
|
+
}
|
|
3432
|
+
];
|
|
3433
|
+
const blockers = [
|
|
3434
|
+
...gates.filter((gate) => !gate.passed).map((gate) => gate.detail),
|
|
3435
|
+
...lanes.flatMap((lane) => lane.blockers || [])
|
|
3436
|
+
];
|
|
3437
|
+
const ready = blockers.length === 0;
|
|
3438
|
+
return {
|
|
3439
|
+
plan,
|
|
3440
|
+
lanes,
|
|
3441
|
+
gates,
|
|
3442
|
+
summary: {
|
|
3443
|
+
ready,
|
|
3444
|
+
score: Math.round(gates.filter((gate) => gate.passed).length / gates.length * 100),
|
|
3445
|
+
targetCount: plan?.targetCount,
|
|
3446
|
+
estimatedCredits: plan?.estimatedCredits,
|
|
3447
|
+
builtInLanesReady: builtInLanes.filter((lane) => lane.ready).length,
|
|
3448
|
+
builtInLanesTotal: builtInLanes.length,
|
|
3449
|
+
customRoutesReady: customRoutes.filter((lane) => lane.ready).length,
|
|
3450
|
+
customRoutesTotal: customRoutes.length,
|
|
3451
|
+
approvalGateCount: firstArray(plan?.approvals).length,
|
|
3452
|
+
blockers,
|
|
3453
|
+
warnings: firstArray(plan?.warnings)
|
|
3454
|
+
},
|
|
3455
|
+
nextActions: ready ? [
|
|
3456
|
+
"signaliz campaign-agent proof --request-file campaign-request.json --json",
|
|
3457
|
+
"signaliz campaign-agent commit-plan --request-file campaign-request.json --json",
|
|
3458
|
+
`signaliz campaign-agent build-approved --request-file campaign-request.json --confirm-launch --approve-all --approved-by operator@example.com --spend-limit ${plan?.estimatedCredits || 0}`
|
|
3459
|
+
] : ["signaliz campaign-agent plan --request-file campaign-request.json --json"]
|
|
3460
|
+
};
|
|
3461
|
+
}
|
|
3462
|
+
function campaignAgentBuiltInForRoute(route) {
|
|
3463
|
+
const map = {
|
|
3464
|
+
"signaliz-lead-generation": "lead_generation",
|
|
3465
|
+
"signaliz-local-leads": "local_leads",
|
|
3466
|
+
"signaliz-email-finding": "email_finding",
|
|
3467
|
+
"signaliz-email-verification": "email_verification",
|
|
3468
|
+
"signaliz-signals": "signals"
|
|
3469
|
+
};
|
|
3470
|
+
return map[String(route?.id || "")];
|
|
3471
|
+
}
|
|
3472
|
+
function campaignAgentReadinessLaneLabel(route, builtIn) {
|
|
3473
|
+
const labels = {
|
|
3474
|
+
lead_generation: "Signaliz lead generation",
|
|
3475
|
+
local_leads: "Signaliz local leads",
|
|
3476
|
+
email_finding: "Signaliz email finding",
|
|
3477
|
+
email_verification: "Signaliz email verification",
|
|
3478
|
+
signals: "Signaliz signals"
|
|
3479
|
+
};
|
|
3480
|
+
return builtIn ? labels[builtIn] : `${route?.provider || "custom"} ${route?.layer || "route"}`;
|
|
3481
|
+
}
|
|
3482
|
+
function printCampaignAgentReadiness(readiness) {
|
|
3483
|
+
const summary = record(readiness.summary);
|
|
3484
|
+
const plan = record(readiness.plan);
|
|
3485
|
+
console.log(`Readiness: ${summary.ready === true ? "ready" : "blocked"} (${summary.score || 0}%)`);
|
|
3486
|
+
if (plan.campaignName) console.log(`Campaign: ${plan.campaignName}`);
|
|
3487
|
+
if (summary.targetCount !== void 0) console.log(`Target: ${summary.targetCount}`);
|
|
3488
|
+
if (summary.estimatedCredits !== void 0) console.log(`Estimated credits: ${summary.estimatedCredits}`);
|
|
3489
|
+
console.log(`Built-ins: ${summary.builtInLanesReady || 0}/${summary.builtInLanesTotal || 0}`);
|
|
3490
|
+
console.log(`BYO routes: ${summary.customRoutesReady || 0}/${summary.customRoutesTotal || 0}`);
|
|
3491
|
+
console.log(`Approval gates: ${summary.approvalGateCount || 0}`);
|
|
3492
|
+
const gates = firstArray(readiness.gates);
|
|
3493
|
+
if (gates.length) {
|
|
3494
|
+
console.log("\nGates:");
|
|
3495
|
+
for (const gate of gates) console.log(`- ${gate.passed ? "PASS" : "BLOCKED"} ${gate.label}: ${gate.detail}`);
|
|
3496
|
+
}
|
|
3497
|
+
const lanes = firstArray(readiness.lanes);
|
|
3498
|
+
if (lanes.length) {
|
|
3499
|
+
console.log("\nLanes:");
|
|
3500
|
+
for (const lane of lanes) console.log(`- ${lane.ready ? "READY" : "BLOCKED"} ${lane.label}: ${lane.toolName || lane.provider}`);
|
|
3501
|
+
}
|
|
3502
|
+
const blockers = firstArray(summary.blockers);
|
|
3503
|
+
if (blockers.length) {
|
|
3504
|
+
console.log("\nBlockers:");
|
|
3505
|
+
for (const blocker of blockers.slice(0, 8)) console.log(`- ${blocker}`);
|
|
3506
|
+
}
|
|
3507
|
+
const nextActions = firstArray(readiness.nextActions);
|
|
3508
|
+
if (nextActions.length) {
|
|
3509
|
+
console.log("\nNext:");
|
|
3510
|
+
for (const action of nextActions) console.log(`- ${action}`);
|
|
3511
|
+
}
|
|
3512
|
+
}
|
|
3308
3513
|
function printCampaignAgentPlan(plan) {
|
|
3309
3514
|
console.log(`Plan: ${plan.campaignName || plan.goal || plan.planId}`);
|
|
3310
3515
|
if (plan.planId) console.log(`Plan ID: ${plan.planId}`);
|
|
@@ -3357,6 +3562,140 @@ function printCampaignAgentBuildStatus(status) {
|
|
|
3357
3562
|
hint(`signaliz ops run-status ${status.triggerRunId} --watch`);
|
|
3358
3563
|
}
|
|
3359
3564
|
}
|
|
3565
|
+
function printCampaignAgentBuildReview(review) {
|
|
3566
|
+
const summary = record(review.summary);
|
|
3567
|
+
const status = record(review.status);
|
|
3568
|
+
console.log(`Campaign: ${status.name || review.campaignBuildId}`);
|
|
3569
|
+
console.log(`Status: ${summary.status || status.status}`);
|
|
3570
|
+
console.log(`Phase: ${summary.phase || status.currentPhase || "N/A"}`);
|
|
3571
|
+
console.log(`Rows: ${summary.sampledRows || 0} sampled, ${summary.availableRows || 0} available`);
|
|
3572
|
+
console.log(`Qualified: ${summary.qualifiedRows || 0}`);
|
|
3573
|
+
console.log(`Emails: ${summary.rowsWithEmail || 0}`);
|
|
3574
|
+
console.log(`Copy ready: ${summary.copyReadyRows || 0}`);
|
|
3575
|
+
console.log(`Artifacts: ${summary.artifactCount || 0}`);
|
|
3576
|
+
console.log(`Delivery approval ready: ${summary.readyForDeliveryApproval === true ? "yes" : "no"}`);
|
|
3577
|
+
const gates = Array.isArray(review.gates) ? review.gates : [];
|
|
3578
|
+
if (gates.length) {
|
|
3579
|
+
console.log("\nGates:");
|
|
3580
|
+
for (const gate of gates) {
|
|
3581
|
+
console.log(`- ${gate.passed ? "PASS" : "BLOCKED"} ${gate.label}: ${gate.detail}`);
|
|
3582
|
+
}
|
|
3583
|
+
}
|
|
3584
|
+
const warnings = Array.isArray(summary.warnings) ? summary.warnings : [];
|
|
3585
|
+
if (warnings.length) {
|
|
3586
|
+
console.log("\nWarnings:");
|
|
3587
|
+
for (const warning of warnings.slice(0, 5)) console.log(`- ${warning}`);
|
|
3588
|
+
}
|
|
3589
|
+
const nextActions = Array.isArray(review.nextActions) ? review.nextActions : [];
|
|
3590
|
+
if (nextActions.length) {
|
|
3591
|
+
console.log("\nNext:");
|
|
3592
|
+
for (const action of nextActions) console.log(`- ${action}`);
|
|
3593
|
+
}
|
|
3594
|
+
}
|
|
3595
|
+
function createCampaignAgentBuildReview(status, rows, artifacts) {
|
|
3596
|
+
const rowList = Array.isArray(rows?.rows) ? rows.rows : [];
|
|
3597
|
+
const sampledRows = rowList.length;
|
|
3598
|
+
const availableRows = Number(rows?.count || sampledRows);
|
|
3599
|
+
const qualifiedRows = rowList.filter((row) => row?.qualified !== false).length;
|
|
3600
|
+
const operatorRows = formatCampaignRowsForOperator(rowList);
|
|
3601
|
+
const rowsWithEmail = operatorRows.filter((row) => Boolean(row.email)).length;
|
|
3602
|
+
const copyReadyRows = operatorRows.filter((row) => Boolean(row.has_copy || row.status === "copy_ready")).length;
|
|
3603
|
+
const artifactCount = artifacts.length || Number(status?.artifactCount || status?.artifact_count || 0);
|
|
3604
|
+
const buildStatus = String(status?.status || "unknown");
|
|
3605
|
+
const gates = [
|
|
3606
|
+
{
|
|
3607
|
+
id: "build_completed",
|
|
3608
|
+
label: "Build completed",
|
|
3609
|
+
passed: buildStatus === "completed",
|
|
3610
|
+
detail: buildStatus === "completed" ? "The campaign build is completed." : `The campaign build is ${buildStatus}; review again after completion.`
|
|
3611
|
+
},
|
|
3612
|
+
{
|
|
3613
|
+
id: "rows_available",
|
|
3614
|
+
label: "Rows available",
|
|
3615
|
+
passed: sampledRows > 0,
|
|
3616
|
+
detail: sampledRows > 0 ? `${sampledRows} sampled row(s) are available for review.` : "No rows were returned for review."
|
|
3617
|
+
},
|
|
3618
|
+
{
|
|
3619
|
+
id: "qualified_sample",
|
|
3620
|
+
label: "Sample rows qualified",
|
|
3621
|
+
passed: sampledRows > 0 && qualifiedRows === sampledRows,
|
|
3622
|
+
detail: sampledRows > 0 ? `${qualifiedRows}/${sampledRows} sampled row(s) are marked qualified.` : "Qualification could not be checked without sampled rows."
|
|
3623
|
+
},
|
|
3624
|
+
{
|
|
3625
|
+
id: "email_coverage",
|
|
3626
|
+
label: "Email coverage",
|
|
3627
|
+
passed: sampledRows > 0 && rowsWithEmail === sampledRows,
|
|
3628
|
+
detail: sampledRows > 0 ? `${rowsWithEmail}/${sampledRows} sampled row(s) include an email.` : "Email coverage could not be checked without sampled rows."
|
|
3629
|
+
},
|
|
3630
|
+
{
|
|
3631
|
+
id: "artifact_available",
|
|
3632
|
+
label: "Artifact available",
|
|
3633
|
+
passed: artifactCount > 0,
|
|
3634
|
+
detail: artifactCount > 0 ? `${artifactCount} artifact(s) are available.` : "No delivery artifact is available yet."
|
|
3635
|
+
},
|
|
3636
|
+
{
|
|
3637
|
+
id: "no_failed_rows",
|
|
3638
|
+
label: "No failed rows",
|
|
3639
|
+
passed: Number(status?.recordsFailed || status?.records_failed || 0) === 0,
|
|
3640
|
+
detail: Number(status?.recordsFailed || status?.records_failed || 0) === 0 ? "The build reports zero failed rows." : `${status.recordsFailed || status.records_failed} row(s) failed during the build.`
|
|
3641
|
+
}
|
|
3642
|
+
];
|
|
3643
|
+
const blockedReasons = gates.filter((gate) => !gate.passed).map((gate) => gate.detail);
|
|
3644
|
+
const warnings = [
|
|
3645
|
+
rows?.hasMore || rows?.has_more ? "This review is based on a row sample; page through remaining rows before final approval." : void 0,
|
|
3646
|
+
sampledRows > 0 && copyReadyRows < sampledRows ? `${copyReadyRows}/${sampledRows} sampled row(s) include generated copy.` : void 0,
|
|
3647
|
+
...Array.isArray(status?.warnings) ? status.warnings : []
|
|
3648
|
+
].filter(Boolean);
|
|
3649
|
+
const readyForDeliveryApproval = blockedReasons.length === 0;
|
|
3650
|
+
return {
|
|
3651
|
+
campaignBuildId: status.campaignBuildId || status.campaign_build_id || rows.campaignBuildId || rows.campaign_build_id || "",
|
|
3652
|
+
status,
|
|
3653
|
+
rows,
|
|
3654
|
+
artifacts,
|
|
3655
|
+
gates,
|
|
3656
|
+
summary: {
|
|
3657
|
+
status: buildStatus,
|
|
3658
|
+
phase: status.currentPhase || status.current_phase || null,
|
|
3659
|
+
sampledRows,
|
|
3660
|
+
availableRows,
|
|
3661
|
+
qualifiedRows,
|
|
3662
|
+
rowsWithEmail,
|
|
3663
|
+
copyReadyRows,
|
|
3664
|
+
artifactCount,
|
|
3665
|
+
readyForDeliveryApproval,
|
|
3666
|
+
blockedReasons,
|
|
3667
|
+
warnings
|
|
3668
|
+
},
|
|
3669
|
+
nextActions: campaignAgentReviewNextActions(status, readyForDeliveryApproval)
|
|
3670
|
+
};
|
|
3671
|
+
}
|
|
3672
|
+
function campaignAgentReviewNextActions(status, readyForDeliveryApproval) {
|
|
3673
|
+
const buildId = status.campaignBuildId || status.campaign_build_id;
|
|
3674
|
+
const buildStatus = String(status.status || "unknown");
|
|
3675
|
+
if (!buildId) return [];
|
|
3676
|
+
if (buildStatus === "completed" && readyForDeliveryApproval) {
|
|
3677
|
+
return [
|
|
3678
|
+
`signaliz campaign-agent approve ${buildId} --destination-type json`,
|
|
3679
|
+
`signaliz campaign-agent artifacts ${buildId}`
|
|
3680
|
+
];
|
|
3681
|
+
}
|
|
3682
|
+
if (buildStatus === "completed") {
|
|
3683
|
+
return [
|
|
3684
|
+
`signaliz campaign-agent rows ${buildId} --limit 100`,
|
|
3685
|
+
`signaliz campaign-agent artifacts ${buildId}`
|
|
3686
|
+
];
|
|
3687
|
+
}
|
|
3688
|
+
if (buildStatus === "pending_approval") {
|
|
3689
|
+
return [
|
|
3690
|
+
`signaliz campaign-agent rows ${buildId} --limit 100`,
|
|
3691
|
+
`signaliz campaign-agent approve ${buildId} --destination-type json`
|
|
3692
|
+
];
|
|
3693
|
+
}
|
|
3694
|
+
if ((buildStatus === "queued" || buildStatus === "running") && status.triggerRunId) {
|
|
3695
|
+
return [`signaliz ops run-status ${status.triggerRunId} --watch`];
|
|
3696
|
+
}
|
|
3697
|
+
return [`signaliz campaign-agent status ${buildId}`];
|
|
3698
|
+
}
|
|
3360
3699
|
function printCampaignAgentRows(result) {
|
|
3361
3700
|
if (!Array.isArray(result.rows) || result.rows.length === 0) {
|
|
3362
3701
|
console.log("No rows found.");
|