deepline 0.1.57 → 0.1.58
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/cli/index.js +1967 -308
- package/dist/cli/index.mjs +1912 -253
- package/dist/helpers.d.mts +30 -0
- package/dist/helpers.d.ts +30 -0
- package/dist/helpers.js +143 -0
- package/dist/helpers.mjs +115 -0
- package/dist/index.d.mts +34 -34
- package/dist/index.d.ts +34 -34
- package/dist/index.js +119 -2
- package/dist/index.mjs +103 -2
- package/dist/repo/sdk/src/index.ts +21 -0
- package/dist/repo/sdk/src/play.ts +2 -18
- package/dist/repo/sdk/src/release.ts +2 -2
- package/dist/repo/sdk/src/types.ts +58 -33
- package/dist/repo/shared_libs/plays/bootstrap-routes.ts +127 -0
- package/dist/repo/shared_libs/plays/bundling/index.ts +3 -0
- package/package.json +1 -1
package/dist/cli/index.mjs
CHANGED
|
@@ -197,10 +197,10 @@ function resolveConfig(options) {
|
|
|
197
197
|
|
|
198
198
|
// src/release.ts
|
|
199
199
|
var SDK_RELEASE = {
|
|
200
|
-
version: "0.1.
|
|
200
|
+
version: "0.1.58",
|
|
201
201
|
apiContract: "2026-05-play-tool-describe-starters",
|
|
202
202
|
supportPolicy: {
|
|
203
|
-
latest: "0.1.
|
|
203
|
+
latest: "0.1.58",
|
|
204
204
|
minimumSupported: "0.1.53",
|
|
205
205
|
deprecatedBelow: "0.1.53"
|
|
206
206
|
}
|
|
@@ -338,8 +338,8 @@ var HttpClient = class {
|
|
|
338
338
|
if (lastError instanceof DeeplineError) {
|
|
339
339
|
throw lastError;
|
|
340
340
|
}
|
|
341
|
-
const
|
|
342
|
-
throw new DeeplineError(
|
|
341
|
+
const errorMessage2 = lastError?.message ? `Unable to connect to ${baseUrl}. ${lastError.message}` : `Unable to connect to ${baseUrl}. Is the computer able to access the url?`;
|
|
342
|
+
throw new DeeplineError(errorMessage2);
|
|
343
343
|
}
|
|
344
344
|
/**
|
|
345
345
|
* Send a GET request.
|
|
@@ -512,7 +512,7 @@ function decodeSseFrame(frame) {
|
|
|
512
512
|
return parsed;
|
|
513
513
|
}
|
|
514
514
|
function sleep(ms) {
|
|
515
|
-
return new Promise((
|
|
515
|
+
return new Promise((resolve12) => setTimeout(resolve12, ms));
|
|
516
516
|
}
|
|
517
517
|
|
|
518
518
|
// src/client.ts
|
|
@@ -522,7 +522,7 @@ var EXECUTE_RESPONSE_CONTRACT_HEADER = "x-deepline-execute-response-contract";
|
|
|
522
522
|
var V2_EXECUTE_RESPONSE_CONTRACT = "v2-tool-response";
|
|
523
523
|
var COMPILE_MANIFEST_RETRY_DELAYS_MS = [250, 1e3];
|
|
524
524
|
function sleep2(ms) {
|
|
525
|
-
return new Promise((
|
|
525
|
+
return new Promise((resolve12) => setTimeout(resolve12, ms));
|
|
526
526
|
}
|
|
527
527
|
function isTransientCompileManifestError(error) {
|
|
528
528
|
if (error instanceof DeeplineError && typeof error.statusCode === "number") {
|
|
@@ -2258,7 +2258,7 @@ function buildCandidateUrls2(url) {
|
|
|
2258
2258
|
}
|
|
2259
2259
|
}
|
|
2260
2260
|
function sleep3(ms) {
|
|
2261
|
-
return new Promise((
|
|
2261
|
+
return new Promise((resolve12) => setTimeout(resolve12, ms));
|
|
2262
2262
|
}
|
|
2263
2263
|
function printDeeplineLogo() {
|
|
2264
2264
|
if (process.stdout.isTTY && (process.stdout.columns ?? 80) >= 70) {
|
|
@@ -4014,7 +4014,7 @@ import {
|
|
|
4014
4014
|
realpathSync,
|
|
4015
4015
|
writeFileSync as writeFileSync6
|
|
4016
4016
|
} from "fs";
|
|
4017
|
-
import { basename as basename3, dirname as dirname8, join as join6, resolve as
|
|
4017
|
+
import { basename as basename3, dirname as dirname8, join as join6, resolve as resolve10 } from "path";
|
|
4018
4018
|
|
|
4019
4019
|
// src/plays/bundle-play-file.ts
|
|
4020
4020
|
import { tmpdir as tmpdir2 } from "os";
|
|
@@ -4374,6 +4374,9 @@ function localSdkAliasPlugin(adapter, options) {
|
|
|
4374
4374
|
buildContext.onResolve({ filter: /^deepline$/ }, () => ({
|
|
4375
4375
|
path: entryFile
|
|
4376
4376
|
}));
|
|
4377
|
+
buildContext.onResolve({ filter: /^deepline\/helpers$/ }, () => ({
|
|
4378
|
+
path: join3(adapter.sdkSourceRoot, "helpers.ts")
|
|
4379
|
+
}));
|
|
4377
4380
|
}
|
|
4378
4381
|
};
|
|
4379
4382
|
}
|
|
@@ -5550,37 +5553,1474 @@ function warnAboutNonDevelopmentBundling(filePath) {
|
|
|
5550
5553
|
console.warn(
|
|
5551
5554
|
`[deepline] Warning: live play bundling was invoked while NODE_ENV=${nodeEnv} for ${filePath}. This source-first SDK path is intended for local development. For preview/production, run a published or prebuilt play reference instead of bundling source at runtime.`
|
|
5552
5555
|
);
|
|
5553
|
-
console.warn(
|
|
5554
|
-
'[deepline] Preferred production call pattern: client.play("person-to-email").run(...) or run a previously registered/published org play.'
|
|
5556
|
+
console.warn(
|
|
5557
|
+
'[deepline] Preferred production call pattern: client.play("person-to-email").run(...) or run a previously registered/published org play.'
|
|
5558
|
+
);
|
|
5559
|
+
}
|
|
5560
|
+
function defaultPlayBundleTarget() {
|
|
5561
|
+
return resolveExecutionProfile(null).artifactKind;
|
|
5562
|
+
}
|
|
5563
|
+
function createSdkPlayBundlingAdapter() {
|
|
5564
|
+
return {
|
|
5565
|
+
projectRoot: PROJECT_ROOT,
|
|
5566
|
+
nodeModulesDir: resolve8(PROJECT_ROOT, "node_modules"),
|
|
5567
|
+
cacheDir: join5(
|
|
5568
|
+
tmpdir2(),
|
|
5569
|
+
`deepline-play-artifacts-v${PLAY_BUNDLE_CACHE_VERSION2}`
|
|
5570
|
+
),
|
|
5571
|
+
sdkSourceRoot: SDK_SOURCE_ROOT,
|
|
5572
|
+
sdkPackageJson: SDK_PACKAGE_JSON,
|
|
5573
|
+
sdkEntryFile: SDK_ENTRY_FILE,
|
|
5574
|
+
sdkTypesEntryFile: HAS_SOURCE_BUNDLING_SOURCES || !existsSync5(SDK_TYPES_ENTRY_FILE) ? SDK_ENTRY_FILE : SDK_TYPES_ENTRY_FILE,
|
|
5575
|
+
sdkWorkersEntryFile: SDK_WORKERS_ENTRY_FILE,
|
|
5576
|
+
workersHarnessEntryFile: WORKERS_HARNESS_ENTRY_FILE,
|
|
5577
|
+
workersHarnessFilesDir: WORKERS_HARNESS_FILES_DIR,
|
|
5578
|
+
discoverPackagedLocalFiles,
|
|
5579
|
+
warnAboutNonDevelopmentBundling
|
|
5580
|
+
};
|
|
5581
|
+
}
|
|
5582
|
+
async function bundlePlayFile2(filePath, options = {}) {
|
|
5583
|
+
return bundlePlayFile(filePath, {
|
|
5584
|
+
target: options.target ?? defaultPlayBundleTarget(),
|
|
5585
|
+
exportName: options.exportName,
|
|
5586
|
+
adapter: createSdkPlayBundlingAdapter()
|
|
5587
|
+
});
|
|
5588
|
+
}
|
|
5589
|
+
|
|
5590
|
+
// src/cli/commands/plays/bootstrap.ts
|
|
5591
|
+
import { closeSync, openSync, readSync, statSync } from "fs";
|
|
5592
|
+
import { isAbsolute as isAbsolute3, relative as relative2, resolve as resolve9 } from "path";
|
|
5593
|
+
import { parse as parseCsvSync } from "csv-parse/sync";
|
|
5594
|
+
|
|
5595
|
+
// ../shared_libs/plays/bootstrap-routes.ts
|
|
5596
|
+
var PLAY_BOOTSTRAP_TEMPLATES = [
|
|
5597
|
+
"people-list",
|
|
5598
|
+
"company-list",
|
|
5599
|
+
"people-email",
|
|
5600
|
+
"people-phone",
|
|
5601
|
+
"company-people",
|
|
5602
|
+
"company-people-email",
|
|
5603
|
+
"company-people-phone"
|
|
5604
|
+
];
|
|
5605
|
+
var PLAY_BOOTSTRAP_COMPANY_PROVIDER_CATEGORY = "company_search";
|
|
5606
|
+
var PLAY_BOOTSTRAP_PEOPLE_PROVIDER_CATEGORY = "people_search";
|
|
5607
|
+
var PLAY_BOOTSTRAP_PROVIDER_CATEGORY_BY_FINDER = {
|
|
5608
|
+
email_finder: "email_finder",
|
|
5609
|
+
phone_finder: "phone_finder"
|
|
5610
|
+
};
|
|
5611
|
+
var PLAY_BOOTSTRAP_OUTPUT_FIELD_BY_FINDER = {
|
|
5612
|
+
email_finder: "email",
|
|
5613
|
+
phone_finder: "phone"
|
|
5614
|
+
};
|
|
5615
|
+
function isPlayBootstrapTemplate(value) {
|
|
5616
|
+
return PLAY_BOOTSTRAP_TEMPLATES.includes(value);
|
|
5617
|
+
}
|
|
5618
|
+
function formatPlayBootstrapTemplates() {
|
|
5619
|
+
return PLAY_BOOTSTRAP_TEMPLATES.join("|");
|
|
5620
|
+
}
|
|
5621
|
+
|
|
5622
|
+
// src/cli/commands/plays/bootstrap.ts
|
|
5623
|
+
function parseReferencedPlayTarget(target) {
|
|
5624
|
+
const trimmed = target.trim();
|
|
5625
|
+
const slashIndex = trimmed.indexOf("/");
|
|
5626
|
+
if (slashIndex <= 0 || slashIndex === trimmed.length - 1) {
|
|
5627
|
+
return { ownerSlug: null, playName: trimmed, unqualifiedPlayName: trimmed };
|
|
5628
|
+
}
|
|
5629
|
+
return {
|
|
5630
|
+
ownerSlug: trimmed.slice(0, slashIndex),
|
|
5631
|
+
playName: trimmed,
|
|
5632
|
+
unqualifiedPlayName: trimmed.slice(slashIndex + 1)
|
|
5633
|
+
};
|
|
5634
|
+
}
|
|
5635
|
+
function parsePositiveInteger2(value, flagName) {
|
|
5636
|
+
const parsed = Number.parseInt(value, 10);
|
|
5637
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
5638
|
+
throw new PlayBootstrapUsageError(
|
|
5639
|
+
`${flagName} must be a positive integer.`
|
|
5640
|
+
);
|
|
5641
|
+
}
|
|
5642
|
+
return parsed;
|
|
5643
|
+
}
|
|
5644
|
+
function isRecord3(value) {
|
|
5645
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
5646
|
+
}
|
|
5647
|
+
function stringValue(value) {
|
|
5648
|
+
return typeof value === "string" ? value.trim() : "";
|
|
5649
|
+
}
|
|
5650
|
+
function extractionEntries(value) {
|
|
5651
|
+
if (Array.isArray(value)) return value.filter(isRecord3);
|
|
5652
|
+
if (!isRecord3(value)) return [];
|
|
5653
|
+
return Object.entries(value).map(
|
|
5654
|
+
([name, entry]) => isRecord3(entry) ? { name, ...entry } : { name }
|
|
5655
|
+
);
|
|
5656
|
+
}
|
|
5657
|
+
var PlayBootstrapError = class extends Error {
|
|
5658
|
+
constructor(message, exitCode) {
|
|
5659
|
+
super(message);
|
|
5660
|
+
this.exitCode = exitCode;
|
|
5661
|
+
}
|
|
5662
|
+
exitCode;
|
|
5663
|
+
};
|
|
5664
|
+
var PlayBootstrapUsageError = class extends PlayBootstrapError {
|
|
5665
|
+
constructor(message) {
|
|
5666
|
+
super(message, 2);
|
|
5667
|
+
}
|
|
5668
|
+
};
|
|
5669
|
+
var PlayBootstrapValidationError = class extends PlayBootstrapError {
|
|
5670
|
+
constructor(message) {
|
|
5671
|
+
super(message, 7);
|
|
5672
|
+
}
|
|
5673
|
+
};
|
|
5674
|
+
var CSV_HEADER_SAMPLE_BYTES = 64 * 1024;
|
|
5675
|
+
function parseCsvList(value) {
|
|
5676
|
+
return value.split(",").map((item) => item.trim()).filter(Boolean);
|
|
5677
|
+
}
|
|
5678
|
+
function parseProviderList(value, flag) {
|
|
5679
|
+
const providers = parseCsvList(value);
|
|
5680
|
+
if (providers.length === 0) {
|
|
5681
|
+
throw new PlayBootstrapUsageError(`${flag} provider list cannot be empty.`);
|
|
5682
|
+
}
|
|
5683
|
+
return providers;
|
|
5684
|
+
}
|
|
5685
|
+
function parseBootstrapPrefixedRef(value, flag) {
|
|
5686
|
+
const separatorIndex = value.indexOf(":");
|
|
5687
|
+
if (separatorIndex <= 0 || separatorIndex === value.length - 1) {
|
|
5688
|
+
throw new PlayBootstrapUsageError(
|
|
5689
|
+
`${flag} expects a typed resource reference like csv:data/leads.csv, play:prebuilt/name-and-domain-to-email-waterfall, provider:dropleads_search_people, or providers:hunter_email_finder,leadmagic_email_finder.`
|
|
5690
|
+
);
|
|
5691
|
+
}
|
|
5692
|
+
return {
|
|
5693
|
+
prefix: value.slice(0, separatorIndex).trim(),
|
|
5694
|
+
body: value.slice(separatorIndex + 1).trim()
|
|
5695
|
+
};
|
|
5696
|
+
}
|
|
5697
|
+
function parseBootstrapSourceRef(value, flag = "--from") {
|
|
5698
|
+
const { prefix, body } = parseBootstrapPrefixedRef(value, flag);
|
|
5699
|
+
switch (prefix) {
|
|
5700
|
+
case "csv":
|
|
5701
|
+
return { kind: "csv", value: body };
|
|
5702
|
+
case "play":
|
|
5703
|
+
return { kind: "play", value: parseReferencedPlayTarget(body).playName };
|
|
5704
|
+
case "provider":
|
|
5705
|
+
case "providers":
|
|
5706
|
+
return { kind: "providers", values: parseProviderList(body, flag) };
|
|
5707
|
+
}
|
|
5708
|
+
throw new PlayBootstrapUsageError(
|
|
5709
|
+
`${flag} does not support ${prefix}:. Use csv:, play:, provider:, or providers:.`
|
|
5710
|
+
);
|
|
5711
|
+
}
|
|
5712
|
+
function parseBootstrapStageRef(value, flag) {
|
|
5713
|
+
const { prefix, body } = parseBootstrapPrefixedRef(value, flag);
|
|
5714
|
+
switch (prefix) {
|
|
5715
|
+
case "play":
|
|
5716
|
+
return { kind: "play", value: parseReferencedPlayTarget(body).playName };
|
|
5717
|
+
case "provider":
|
|
5718
|
+
case "providers":
|
|
5719
|
+
return { kind: "providers", values: parseProviderList(body, flag) };
|
|
5720
|
+
}
|
|
5721
|
+
throw new PlayBootstrapUsageError(
|
|
5722
|
+
`${flag} does not support ${prefix}:. Use play:, provider:, or providers:.`
|
|
5723
|
+
);
|
|
5724
|
+
}
|
|
5725
|
+
function playBootstrapTemplateConfig(template) {
|
|
5726
|
+
switch (template) {
|
|
5727
|
+
case "company-list":
|
|
5728
|
+
return { sourceEntity: "company", usingStage: null, requiredStages: [] };
|
|
5729
|
+
case "people-list":
|
|
5730
|
+
return { sourceEntity: "people", usingStage: null, requiredStages: [] };
|
|
5731
|
+
case "people-email":
|
|
5732
|
+
return {
|
|
5733
|
+
sourceEntity: "people",
|
|
5734
|
+
usingStage: "email",
|
|
5735
|
+
requiredStages: []
|
|
5736
|
+
};
|
|
5737
|
+
case "people-phone":
|
|
5738
|
+
return {
|
|
5739
|
+
sourceEntity: "people",
|
|
5740
|
+
usingStage: "phone",
|
|
5741
|
+
requiredStages: []
|
|
5742
|
+
};
|
|
5743
|
+
case "company-people":
|
|
5744
|
+
return {
|
|
5745
|
+
sourceEntity: "company",
|
|
5746
|
+
usingStage: "people",
|
|
5747
|
+
requiredStages: []
|
|
5748
|
+
};
|
|
5749
|
+
case "company-people-email":
|
|
5750
|
+
return {
|
|
5751
|
+
sourceEntity: "company",
|
|
5752
|
+
usingStage: null,
|
|
5753
|
+
requiredStages: ["people", "email"]
|
|
5754
|
+
};
|
|
5755
|
+
case "company-people-phone":
|
|
5756
|
+
return {
|
|
5757
|
+
sourceEntity: "company",
|
|
5758
|
+
usingStage: null,
|
|
5759
|
+
requiredStages: ["people", "phone"]
|
|
5760
|
+
};
|
|
5761
|
+
}
|
|
5762
|
+
}
|
|
5763
|
+
function templateExample(template) {
|
|
5764
|
+
switch (template) {
|
|
5765
|
+
case "people-list":
|
|
5766
|
+
return "deepline plays bootstrap people-list --from provider:dropleads_search_people > people.play.ts";
|
|
5767
|
+
case "company-list":
|
|
5768
|
+
return "deepline plays bootstrap company-list --from provider:apollo_company_search > companies.play.ts";
|
|
5769
|
+
case "people-email":
|
|
5770
|
+
return "deepline plays bootstrap people-email --from csv:data/leads.csv --using play:prebuilt/name-and-domain-to-email-waterfall > email-flow.play.ts";
|
|
5771
|
+
case "people-phone":
|
|
5772
|
+
return "deepline plays bootstrap people-phone --from csv:data/vp_contacts.csv --using play:prebuilt/person-to-phone > phone-flow.play.ts";
|
|
5773
|
+
case "company-people":
|
|
5774
|
+
return "deepline plays bootstrap company-people --from provider:apollo_company_search --using play:prebuilt/company-to-contact > company-people.play.ts";
|
|
5775
|
+
case "company-people-email":
|
|
5776
|
+
return "deepline plays bootstrap company-people-email --from provider:apollo_company_search --people play:prebuilt/company-to-contact --email providers:hunter_email_finder,leadmagic_email_finder > account-emails.play.ts";
|
|
5777
|
+
case "company-people-phone":
|
|
5778
|
+
return "deepline plays bootstrap company-people-phone --from provider:apollo_company_search --people play:prebuilt/company-to-contact --phone providers:ai_ark_mobile_phone_finder > account-phones.play.ts";
|
|
5779
|
+
}
|
|
5780
|
+
}
|
|
5781
|
+
var PLAY_BOOTSTRAP_STAGE_NAMES = [
|
|
5782
|
+
"people",
|
|
5783
|
+
"email",
|
|
5784
|
+
"phone"
|
|
5785
|
+
];
|
|
5786
|
+
function playBootstrapUsageLine() {
|
|
5787
|
+
return `Usage: deepline plays bootstrap <${formatPlayBootstrapTemplates()}> --from <csv:PATH|play:REF|provider:ID|providers:ID,ID> [--using <play:REF|providers:ID,ID>] [--people play:REF] [--email <play:REF|providers:ID,ID>] [--phone <play:REF|providers:ID,ID>] [--limit 5] > flow.play.ts`;
|
|
5788
|
+
}
|
|
5789
|
+
function requireBootstrapTemplate(rawTemplate) {
|
|
5790
|
+
if (!rawTemplate) {
|
|
5791
|
+
throw new PlayBootstrapUsageError(
|
|
5792
|
+
`plays bootstrap needs a route template: ${formatPlayBootstrapTemplates()}.
|
|
5793
|
+
Example: deepline plays bootstrap people-email --from csv:data/leads.csv --using play:prebuilt/name-and-domain-to-email-waterfall > email-flow.play.ts`
|
|
5794
|
+
);
|
|
5795
|
+
}
|
|
5796
|
+
if (!isPlayBootstrapTemplate(rawTemplate)) {
|
|
5797
|
+
throw new PlayBootstrapUsageError(
|
|
5798
|
+
`Unknown plays bootstrap template: ${rawTemplate}
|
|
5799
|
+
Supported templates: ${formatPlayBootstrapTemplates()}`
|
|
5800
|
+
);
|
|
5801
|
+
}
|
|
5802
|
+
return rawTemplate;
|
|
5803
|
+
}
|
|
5804
|
+
function nextFlagValue(args, index, flag) {
|
|
5805
|
+
const value = args[index + 1];
|
|
5806
|
+
if (!value || value.startsWith("--")) {
|
|
5807
|
+
throw new PlayBootstrapUsageError(`${flag} needs a value.`);
|
|
5808
|
+
}
|
|
5809
|
+
return value;
|
|
5810
|
+
}
|
|
5811
|
+
function stageFlag(stage) {
|
|
5812
|
+
return `--${stage}`;
|
|
5813
|
+
}
|
|
5814
|
+
function stageRefHelp(stage) {
|
|
5815
|
+
switch (stage) {
|
|
5816
|
+
case "people":
|
|
5817
|
+
return "play:<people-play>";
|
|
5818
|
+
case "email":
|
|
5819
|
+
case "phone":
|
|
5820
|
+
return "<play:REF|provider:ID|providers:ID,ID>";
|
|
5821
|
+
}
|
|
5822
|
+
}
|
|
5823
|
+
function getStageRef(options, stage) {
|
|
5824
|
+
switch (stage) {
|
|
5825
|
+
case "people":
|
|
5826
|
+
return options.people;
|
|
5827
|
+
case "email":
|
|
5828
|
+
return options.email;
|
|
5829
|
+
case "phone":
|
|
5830
|
+
return options.phone;
|
|
5831
|
+
}
|
|
5832
|
+
}
|
|
5833
|
+
function setStageRef(options, stage, ref) {
|
|
5834
|
+
switch (stage) {
|
|
5835
|
+
case "people":
|
|
5836
|
+
options.people = ref;
|
|
5837
|
+
return;
|
|
5838
|
+
case "email":
|
|
5839
|
+
options.email = ref;
|
|
5840
|
+
return;
|
|
5841
|
+
case "phone":
|
|
5842
|
+
options.phone = ref;
|
|
5843
|
+
return;
|
|
5844
|
+
}
|
|
5845
|
+
}
|
|
5846
|
+
function unsupportedStageMessage(template, stage) {
|
|
5847
|
+
switch (stage) {
|
|
5848
|
+
case "people":
|
|
5849
|
+
return `${template} does not accept --people. Use company-people, company-people-email, or company-people-phone.`;
|
|
5850
|
+
case "email":
|
|
5851
|
+
return `${template} does not accept --email. Use people-email or company-people-email.`;
|
|
5852
|
+
case "phone":
|
|
5853
|
+
return `${template} does not accept --phone. Use people-phone or company-people-phone.`;
|
|
5854
|
+
}
|
|
5855
|
+
}
|
|
5856
|
+
function requireSource(options) {
|
|
5857
|
+
if (!options.from) {
|
|
5858
|
+
throw new PlayBootstrapUsageError(
|
|
5859
|
+
`${options.template} needs --from.
|
|
5860
|
+
Example: ${templateExample(options.template)}`
|
|
5861
|
+
);
|
|
5862
|
+
}
|
|
5863
|
+
return options.from;
|
|
5864
|
+
}
|
|
5865
|
+
function assertAllowedStageFlags(options, allowedStages) {
|
|
5866
|
+
const allowed = new Set(allowedStages);
|
|
5867
|
+
for (const stage of PLAY_BOOTSTRAP_STAGE_NAMES) {
|
|
5868
|
+
if (!allowed.has(stage) && getStageRef(options, stage)) {
|
|
5869
|
+
throw new PlayBootstrapUsageError(
|
|
5870
|
+
unsupportedStageMessage(options.template, stage)
|
|
5871
|
+
);
|
|
5872
|
+
}
|
|
5873
|
+
}
|
|
5874
|
+
}
|
|
5875
|
+
function assertRequiredStages(options, stages) {
|
|
5876
|
+
for (const stage of stages) {
|
|
5877
|
+
if (!getStageRef(options, stage)) {
|
|
5878
|
+
throw new PlayBootstrapUsageError(
|
|
5879
|
+
`${options.template} needs ${stageFlag(stage)} ${stageRefHelp(stage)}.
|
|
5880
|
+
Example: ${templateExample(options.template)}`
|
|
5881
|
+
);
|
|
5882
|
+
}
|
|
5883
|
+
}
|
|
5884
|
+
}
|
|
5885
|
+
function normalizeSingleStageRoute(options, stage) {
|
|
5886
|
+
const explicitStage = getStageRef(options, stage);
|
|
5887
|
+
if (options.using && explicitStage) {
|
|
5888
|
+
throw new PlayBootstrapUsageError(
|
|
5889
|
+
`${options.template} received both --using and ${stageFlag(stage)}. Choose one stage binding.`
|
|
5890
|
+
);
|
|
5891
|
+
}
|
|
5892
|
+
const selectedStage = options.using ?? explicitStage;
|
|
5893
|
+
if (!selectedStage) {
|
|
5894
|
+
throw new PlayBootstrapUsageError(
|
|
5895
|
+
`${options.template} needs --using ${stageRefHelp(stage)} or ${stageFlag(stage)} ${stageRefHelp(stage)}.
|
|
5896
|
+
Example: ${templateExample(options.template)}`
|
|
5897
|
+
);
|
|
5898
|
+
}
|
|
5899
|
+
setStageRef(options, stage, selectedStage);
|
|
5900
|
+
options.using = selectedStage;
|
|
5901
|
+
assertAllowedStageFlags(options, [stage]);
|
|
5902
|
+
}
|
|
5903
|
+
function normalizeMultiStageRoute(options, stages) {
|
|
5904
|
+
if (options.using) {
|
|
5905
|
+
throw new PlayBootstrapUsageError(
|
|
5906
|
+
`${options.template} does not accept --using. Use the explicit stage flags shown in: ${templateExample(options.template)}`
|
|
5907
|
+
);
|
|
5908
|
+
}
|
|
5909
|
+
assertRequiredStages(options, stages);
|
|
5910
|
+
assertAllowedStageFlags(options, stages);
|
|
5911
|
+
}
|
|
5912
|
+
function normalizeTemplateStages(options) {
|
|
5913
|
+
const config = playBootstrapTemplateConfig(options.template);
|
|
5914
|
+
const from = requireSource(options);
|
|
5915
|
+
switch (config.usingStage) {
|
|
5916
|
+
case "people":
|
|
5917
|
+
case "email":
|
|
5918
|
+
case "phone":
|
|
5919
|
+
normalizeSingleStageRoute(options, config.usingStage);
|
|
5920
|
+
break;
|
|
5921
|
+
case null:
|
|
5922
|
+
normalizeMultiStageRoute(options, config.requiredStages);
|
|
5923
|
+
break;
|
|
5924
|
+
}
|
|
5925
|
+
return {
|
|
5926
|
+
...options,
|
|
5927
|
+
from
|
|
5928
|
+
};
|
|
5929
|
+
}
|
|
5930
|
+
function parsePlayBootstrapOptions(args) {
|
|
5931
|
+
const [rawTemplate, ...rest] = args;
|
|
5932
|
+
const template = requireBootstrapTemplate(rawTemplate);
|
|
5933
|
+
const options = {
|
|
5934
|
+
template,
|
|
5935
|
+
name: `gtm-${template}`,
|
|
5936
|
+
from: null,
|
|
5937
|
+
using: null,
|
|
5938
|
+
people: null,
|
|
5939
|
+
email: null,
|
|
5940
|
+
phone: null,
|
|
5941
|
+
limit: 5
|
|
5942
|
+
};
|
|
5943
|
+
for (let index = 0; index < rest.length; index += 1) {
|
|
5944
|
+
const arg = rest[index];
|
|
5945
|
+
const value = () => nextFlagValue(rest, index, arg);
|
|
5946
|
+
switch (arg) {
|
|
5947
|
+
case "--name":
|
|
5948
|
+
options.name = value();
|
|
5949
|
+
index += 1;
|
|
5950
|
+
break;
|
|
5951
|
+
case "--from":
|
|
5952
|
+
options.from = parseBootstrapSourceRef(value(), "--from");
|
|
5953
|
+
index += 1;
|
|
5954
|
+
break;
|
|
5955
|
+
case "--using":
|
|
5956
|
+
options.using = parseBootstrapStageRef(value(), "--using");
|
|
5957
|
+
index += 1;
|
|
5958
|
+
break;
|
|
5959
|
+
case "--people":
|
|
5960
|
+
options.people = parseBootstrapStageRef(value(), "--people");
|
|
5961
|
+
index += 1;
|
|
5962
|
+
break;
|
|
5963
|
+
case "--email":
|
|
5964
|
+
options.email = parseBootstrapStageRef(value(), "--email");
|
|
5965
|
+
index += 1;
|
|
5966
|
+
break;
|
|
5967
|
+
case "--phone":
|
|
5968
|
+
options.phone = parseBootstrapStageRef(value(), "--phone");
|
|
5969
|
+
index += 1;
|
|
5970
|
+
break;
|
|
5971
|
+
case "--limit":
|
|
5972
|
+
options.limit = parsePositiveInteger2(value(), "--limit");
|
|
5973
|
+
index += 1;
|
|
5974
|
+
break;
|
|
5975
|
+
default:
|
|
5976
|
+
throw new PlayBootstrapUsageError(
|
|
5977
|
+
`Unknown plays bootstrap option: ${arg}
|
|
5978
|
+
${playBootstrapUsageLine()}`
|
|
5979
|
+
);
|
|
5980
|
+
}
|
|
5981
|
+
}
|
|
5982
|
+
return normalizeTemplateStages(options);
|
|
5983
|
+
}
|
|
5984
|
+
function jsString(value) {
|
|
5985
|
+
return JSON.stringify(value);
|
|
5986
|
+
}
|
|
5987
|
+
function inferCsvCellTypeExpression(values) {
|
|
5988
|
+
const nonEmptyValues = values.map((value) => value.trim()).filter((value) => value.length > 0);
|
|
5989
|
+
const hasEmptyValue = values.some((value) => value.trim().length === 0);
|
|
5990
|
+
if (nonEmptyValues.length === 0) return "string";
|
|
5991
|
+
const numericPattern = /^[+-]?(?:(?:\d+\.?\d*)|(?:\.\d+))(?:[eE][+-]?\d+)?$/;
|
|
5992
|
+
const booleanPattern = /^(?:true|false)$/i;
|
|
5993
|
+
let inferred = "string";
|
|
5994
|
+
if (nonEmptyValues.every((value) => numericPattern.test(value))) {
|
|
5995
|
+
inferred = "`${number}`";
|
|
5996
|
+
} else if (nonEmptyValues.every((value) => booleanPattern.test(value))) {
|
|
5997
|
+
const canonicalBooleanValues = nonEmptyValues.every(
|
|
5998
|
+
(value) => value === "true" || value === "false"
|
|
5999
|
+
);
|
|
6000
|
+
inferred = canonicalBooleanValues ? '"false" | "true"' : [...new Set(nonEmptyValues)].sort().map(jsString).join(" | ");
|
|
6001
|
+
}
|
|
6002
|
+
return hasEmptyValue && inferred !== "string" ? `${inferred} | ""` : inferred;
|
|
6003
|
+
}
|
|
6004
|
+
function inferCsvColumnSpecs(headers, rows) {
|
|
6005
|
+
return headers.map((header) => ({
|
|
6006
|
+
name: header,
|
|
6007
|
+
typeExpression: inferCsvCellTypeExpression(
|
|
6008
|
+
rows.map((row) => String(row[header] ?? ""))
|
|
6009
|
+
)
|
|
6010
|
+
}));
|
|
6011
|
+
}
|
|
6012
|
+
function readCsvSample(csvPath) {
|
|
6013
|
+
const resolvedPath = resolve9(csvPath);
|
|
6014
|
+
const size = statSync(resolvedPath).size;
|
|
6015
|
+
const fd = openSync(resolvedPath, "r");
|
|
6016
|
+
const byteLength = Math.min(size, CSV_HEADER_SAMPLE_BYTES);
|
|
6017
|
+
const buffer = Buffer.alloc(byteLength);
|
|
6018
|
+
const bytesRead = readSync(fd, buffer, 0, byteLength, 0);
|
|
6019
|
+
closeSync(fd);
|
|
6020
|
+
if (bytesRead === 0) {
|
|
6021
|
+
throw new PlayBootstrapUsageError(`--from csv:${csvPath} is empty.`);
|
|
6022
|
+
}
|
|
6023
|
+
return {
|
|
6024
|
+
content: buffer.subarray(0, bytesRead).toString("utf8"),
|
|
6025
|
+
truncated: size > bytesRead
|
|
6026
|
+
};
|
|
6027
|
+
}
|
|
6028
|
+
function completeCsvSample(csvPath, sample) {
|
|
6029
|
+
if (!sample.truncated) return sample.content;
|
|
6030
|
+
const lastLineBreak = Math.max(
|
|
6031
|
+
sample.content.lastIndexOf("\n"),
|
|
6032
|
+
sample.content.lastIndexOf("\r")
|
|
6033
|
+
);
|
|
6034
|
+
if (lastLineBreak < 0) {
|
|
6035
|
+
throw new PlayBootstrapUsageError(
|
|
6036
|
+
`CSV header in ${csvPath} is longer than ${CSV_HEADER_SAMPLE_BYTES} bytes; shorten the header row before bootstrapping.`
|
|
6037
|
+
);
|
|
6038
|
+
}
|
|
6039
|
+
return sample.content.slice(0, lastLineBreak + 1);
|
|
6040
|
+
}
|
|
6041
|
+
function readCsvHeaderFields(csvPath, sample) {
|
|
6042
|
+
const records = parseCsvSync(sample, {
|
|
6043
|
+
bom: true,
|
|
6044
|
+
to_line: 1,
|
|
6045
|
+
relax_column_count: true,
|
|
6046
|
+
skip_empty_lines: false
|
|
6047
|
+
});
|
|
6048
|
+
const firstRecord = Array.isArray(records) ? records[0] : null;
|
|
6049
|
+
const fields = Array.isArray(firstRecord) ? firstRecord.map((field) => String(field ?? "").trim()).filter(Boolean) : [];
|
|
6050
|
+
const uniqueFields = [...new Set(fields)];
|
|
6051
|
+
if (uniqueFields.length === 0) {
|
|
6052
|
+
throw new PlayBootstrapUsageError(
|
|
6053
|
+
`Could not read a header row from --from csv:${csvPath}.`
|
|
6054
|
+
);
|
|
6055
|
+
}
|
|
6056
|
+
return uniqueFields;
|
|
6057
|
+
}
|
|
6058
|
+
function readCsvSampleRows(sample) {
|
|
6059
|
+
const parsedRows = parseCsvSync(sample, {
|
|
6060
|
+
bom: true,
|
|
6061
|
+
columns: true,
|
|
6062
|
+
skip_empty_lines: true,
|
|
6063
|
+
relax_column_count: true,
|
|
6064
|
+
trim: true
|
|
6065
|
+
});
|
|
6066
|
+
return Array.isArray(parsedRows) ? parsedRows.filter(isRecord3) : [];
|
|
6067
|
+
}
|
|
6068
|
+
function readSourceCsvColumnSpecs(csvPath) {
|
|
6069
|
+
const sample = readCsvSample(csvPath);
|
|
6070
|
+
const completeSample = completeCsvSample(csvPath, sample);
|
|
6071
|
+
return inferCsvColumnSpecs(
|
|
6072
|
+
readCsvHeaderFields(csvPath, sample.content),
|
|
6073
|
+
readCsvSampleRows(completeSample)
|
|
6074
|
+
);
|
|
6075
|
+
}
|
|
6076
|
+
function renderSourceCsvRowType(columns) {
|
|
6077
|
+
if (columns.length === 0) return "";
|
|
6078
|
+
const properties = columns.map((column) => ` ${jsString(column.name)}: ${column.typeExpression};`).join("\n");
|
|
6079
|
+
return `// CSV cells are strings at runtime. \`${"${number}"}\` means a numeric-looking CSV string; cast before math.
|
|
6080
|
+
type SourceCsvRow = {
|
|
6081
|
+
${properties}
|
|
6082
|
+
};
|
|
6083
|
+
|
|
6084
|
+
`;
|
|
6085
|
+
}
|
|
6086
|
+
function packagedCsvPathForPlay(csvPath) {
|
|
6087
|
+
const playDir = process.cwd();
|
|
6088
|
+
const absoluteCsvPath = resolve9(csvPath);
|
|
6089
|
+
const relativePath = relative2(playDir, absoluteCsvPath);
|
|
6090
|
+
if (relativePath === "" || relativePath.startsWith("..") || isAbsolute3(relativePath)) {
|
|
6091
|
+
throw new PlayBootstrapUsageError(
|
|
6092
|
+
`--from csv:${csvPath} must point to a file inside the directory where you run plays bootstrap. Run bootstrap from the intended play directory, then redirect stdout to a .play.ts file there.`
|
|
6093
|
+
);
|
|
6094
|
+
}
|
|
6095
|
+
const portablePath = relativePath.split("\\").join("/");
|
|
6096
|
+
return portablePath.startsWith(".") ? portablePath : `./${portablePath}`;
|
|
6097
|
+
}
|
|
6098
|
+
function getterNamesFromTool(tool, kind) {
|
|
6099
|
+
const usageGuidance = isRecord3(tool?.usageGuidance) ? tool.usageGuidance : {};
|
|
6100
|
+
const resultGuidance = isRecord3(usageGuidance.toolExecutionResult) ? usageGuidance.toolExecutionResult : isRecord3(usageGuidance.tool_execution_result) ? usageGuidance.tool_execution_result : {};
|
|
6101
|
+
const key = kind === "list" ? "extractedLists" : "extractedValues";
|
|
6102
|
+
const snakeKey = kind === "list" ? "extracted_lists" : "extracted_values";
|
|
6103
|
+
return extractionEntries(resultGuidance[key] ?? resultGuidance[snakeKey]).map((entry) => stringValue(entry.name)).filter(Boolean);
|
|
6104
|
+
}
|
|
6105
|
+
function targetGettersFromTool(tool) {
|
|
6106
|
+
const record = isRecord3(tool) ? tool : {};
|
|
6107
|
+
const raw = record.targetGetters ?? record.target_getters;
|
|
6108
|
+
if (!isRecord3(raw)) return {};
|
|
6109
|
+
const entries = [];
|
|
6110
|
+
for (const [target, value] of Object.entries(raw)) {
|
|
6111
|
+
const paths = Array.isArray(value) ? value.map((path) => typeof path === "string" ? path.trim() : "").filter(Boolean) : [];
|
|
6112
|
+
if (target.trim() && paths.length > 0) {
|
|
6113
|
+
entries.push([target.trim(), paths]);
|
|
6114
|
+
}
|
|
6115
|
+
}
|
|
6116
|
+
return Object.fromEntries(entries);
|
|
6117
|
+
}
|
|
6118
|
+
function listRowCandidateKeysFromTool(tool) {
|
|
6119
|
+
const keys = /* @__PURE__ */ new Set();
|
|
6120
|
+
for (const paths of Object.values(targetGettersFromTool(tool))) {
|
|
6121
|
+
for (const path of paths) {
|
|
6122
|
+
const key = path.replace(/\[(?:\*|\d+)\]/g, "").split(/[.[\]]/, 1)[0]?.trim();
|
|
6123
|
+
if (key) keys.add(key);
|
|
6124
|
+
}
|
|
6125
|
+
}
|
|
6126
|
+
return [...keys].sort();
|
|
6127
|
+
}
|
|
6128
|
+
function inputPropertyNames(schema) {
|
|
6129
|
+
if (!isRecord3(schema)) return [];
|
|
6130
|
+
if (isRecord3(schema.properties)) return Object.keys(schema.properties);
|
|
6131
|
+
if (Array.isArray(schema.fields)) {
|
|
6132
|
+
return schema.fields.filter(isRecord3).map((field) => stringValue(field.name)).filter(Boolean);
|
|
6133
|
+
}
|
|
6134
|
+
return [];
|
|
6135
|
+
}
|
|
6136
|
+
function schemaFieldDetails(schema) {
|
|
6137
|
+
const required = requiredPlayInputFields({
|
|
6138
|
+
inputSchema: schema
|
|
6139
|
+
});
|
|
6140
|
+
const optional = inputPropertyNames(schema).filter(
|
|
6141
|
+
(field) => !required.includes(field)
|
|
6142
|
+
);
|
|
6143
|
+
return { required, optional };
|
|
6144
|
+
}
|
|
6145
|
+
function jsonSchemaTypeExpression(schema) {
|
|
6146
|
+
if (!isRecord3(schema)) return "unknown";
|
|
6147
|
+
const type = schema.type;
|
|
6148
|
+
if (Array.isArray(type)) {
|
|
6149
|
+
return type.map((entry) => jsonSchemaTypeExpression({ ...schema, type: entry })).join(" | ");
|
|
6150
|
+
}
|
|
6151
|
+
if (Array.isArray(schema.anyOf)) {
|
|
6152
|
+
return schema.anyOf.map(jsonSchemaTypeExpression).join(" | ");
|
|
6153
|
+
}
|
|
6154
|
+
if (Array.isArray(schema.oneOf)) {
|
|
6155
|
+
return schema.oneOf.map(jsonSchemaTypeExpression).join(" | ");
|
|
6156
|
+
}
|
|
6157
|
+
switch (type) {
|
|
6158
|
+
case "string":
|
|
6159
|
+
return "string";
|
|
6160
|
+
case "number":
|
|
6161
|
+
case "integer":
|
|
6162
|
+
return "number";
|
|
6163
|
+
case "boolean":
|
|
6164
|
+
return "boolean";
|
|
6165
|
+
case "null":
|
|
6166
|
+
return "null";
|
|
6167
|
+
case "array":
|
|
6168
|
+
return `Array<${jsonSchemaTypeExpression(schema.items)}>`;
|
|
6169
|
+
case "object":
|
|
6170
|
+
return "Record<string, unknown>";
|
|
6171
|
+
default:
|
|
6172
|
+
return "unknown";
|
|
6173
|
+
}
|
|
6174
|
+
}
|
|
6175
|
+
function objectPropertySchema(schema, property) {
|
|
6176
|
+
return isRecord3(schema) && isRecord3(schema.properties) ? schema.properties[property] : null;
|
|
6177
|
+
}
|
|
6178
|
+
function finderResultTypeName(finder) {
|
|
6179
|
+
return finder === "email_finder" ? "EmailFinderPlayResult" : "PhoneFinderPlayResult";
|
|
6180
|
+
}
|
|
6181
|
+
function renderFinderPlayResultType(input) {
|
|
6182
|
+
if (!input.play) return null;
|
|
6183
|
+
const outputField = PLAY_BOOTSTRAP_OUTPUT_FIELD_BY_FINDER[input.finder];
|
|
6184
|
+
const fieldSchema = objectPropertySchema(
|
|
6185
|
+
input.play.outputSchema,
|
|
6186
|
+
outputField
|
|
6187
|
+
);
|
|
6188
|
+
const fieldType = fieldSchema ? jsonSchemaTypeExpression(fieldSchema) : "string | null";
|
|
6189
|
+
return `type ${finderResultTypeName(input.finder)} =
|
|
6190
|
+
| string
|
|
6191
|
+
| null
|
|
6192
|
+
| {
|
|
6193
|
+
${outputField}?: ${fieldType};
|
|
6194
|
+
};`;
|
|
6195
|
+
}
|
|
6196
|
+
function generatedFinderPlayResultTypes(input) {
|
|
6197
|
+
return ["email_finder", "phone_finder"].flatMap((finder) => {
|
|
6198
|
+
const stage = finderStage(input.options, finder);
|
|
6199
|
+
if (stage?.kind !== "play") return [];
|
|
6200
|
+
const typeDefinition = renderFinderPlayResultType({
|
|
6201
|
+
finder,
|
|
6202
|
+
play: input.finderPlays[finder]
|
|
6203
|
+
});
|
|
6204
|
+
return typeDefinition ? [typeDefinition] : [];
|
|
6205
|
+
}).join("\n\n");
|
|
6206
|
+
}
|
|
6207
|
+
function exampleValueComment(field) {
|
|
6208
|
+
if (field === "limit" || field === "numResults" || field === "num_results" || field === "page_size") {
|
|
6209
|
+
return "limit";
|
|
6210
|
+
}
|
|
6211
|
+
if (field === "roles" || field.endsWith("s")) return '["..."]';
|
|
6212
|
+
return '"..."';
|
|
6213
|
+
}
|
|
6214
|
+
function generateContactInputObjectFromSchema(schema, indent, label, fallbackFields = ["first_name", "last_name", "domain"]) {
|
|
6215
|
+
const details = schemaFieldDetails(schema);
|
|
6216
|
+
const required = details.required.length ? details.required : fallbackFields;
|
|
6217
|
+
const optional = details.optional;
|
|
6218
|
+
const lines = [
|
|
6219
|
+
`${indent}// TODO: map row fields into ${label}.`,
|
|
6220
|
+
...playInspectionComments(label, indent),
|
|
6221
|
+
`${indent}// Required: ${required.join(", ") || "none declared"}.`
|
|
6222
|
+
];
|
|
6223
|
+
for (const field of required) {
|
|
6224
|
+
lines.push(`${indent}// ${field}: row["TODO_SOURCE_FIELD"],`);
|
|
6225
|
+
}
|
|
6226
|
+
if (optional.length > 0) {
|
|
6227
|
+
lines.push("");
|
|
6228
|
+
lines.push(`${indent}// optional (delete unused):`);
|
|
6229
|
+
for (const field of optional) {
|
|
6230
|
+
lines.push(`${indent}// ${field}: row["TODO_SOURCE_FIELD"],`);
|
|
6231
|
+
}
|
|
6232
|
+
}
|
|
6233
|
+
return `{
|
|
6234
|
+
${lines.join("\n")}
|
|
6235
|
+
${indent.slice(2)}}`;
|
|
6236
|
+
}
|
|
6237
|
+
function generateCompanyInputObjectFromSchema(schema, indent, label, fallbackFields = ["domain", "company_name"]) {
|
|
6238
|
+
const details = schemaFieldDetails(schema);
|
|
6239
|
+
const required = details.required.length ? details.required : fallbackFields;
|
|
6240
|
+
const optional = details.optional;
|
|
6241
|
+
const lines = [
|
|
6242
|
+
`${indent}// TODO: map company fields into ${label}.`,
|
|
6243
|
+
...playInspectionComments(label, indent),
|
|
6244
|
+
`${indent}// Required: ${required.join(", ") || "none declared"}.`
|
|
6245
|
+
];
|
|
6246
|
+
for (const field of required) {
|
|
6247
|
+
lines.push(`${indent}// ${field}: company["TODO_SOURCE_FIELD"],`);
|
|
6248
|
+
}
|
|
6249
|
+
if (optional.length > 0) {
|
|
6250
|
+
lines.push("");
|
|
6251
|
+
lines.push(`${indent}// optional (delete unused):`);
|
|
6252
|
+
for (const field of optional) {
|
|
6253
|
+
lines.push(`${indent}// ${field}: company["TODO_SOURCE_FIELD"],`);
|
|
6254
|
+
}
|
|
6255
|
+
}
|
|
6256
|
+
return `{
|
|
6257
|
+
${lines.join("\n")}
|
|
6258
|
+
${indent.slice(2)}}`;
|
|
6259
|
+
}
|
|
6260
|
+
function generateSourceProviderInputObject(input) {
|
|
6261
|
+
const { tool, indent, label, entity } = input;
|
|
6262
|
+
const properties = inputPropertyNames(tool?.inputSchema);
|
|
6263
|
+
const details = schemaFieldDetails(tool?.inputSchema);
|
|
6264
|
+
const required = details.required;
|
|
6265
|
+
const includeOptional = properties.length === 0 ? ["query", "title", "domain", "limit"] : properties.filter(
|
|
6266
|
+
(field) => [
|
|
6267
|
+
"query",
|
|
6268
|
+
"q",
|
|
6269
|
+
"search",
|
|
6270
|
+
"title",
|
|
6271
|
+
"role",
|
|
6272
|
+
"persona",
|
|
6273
|
+
"domain",
|
|
6274
|
+
"company_domain",
|
|
6275
|
+
"limit",
|
|
6276
|
+
"numResults",
|
|
6277
|
+
"num_results",
|
|
6278
|
+
"page_size"
|
|
6279
|
+
].includes(field)
|
|
6280
|
+
);
|
|
6281
|
+
const lines = [
|
|
6282
|
+
`${indent}// TODO: fill ${entity} source inputs for ${label}.`,
|
|
6283
|
+
`${indent}// Inspect: deepline tools describe ${label} --json`
|
|
6284
|
+
];
|
|
6285
|
+
for (const field of required) {
|
|
6286
|
+
lines.push(`${indent}// ${field}: ${exampleValueComment(field)},`);
|
|
6287
|
+
}
|
|
6288
|
+
const activeTodoField = required.length === 0 ? ["query", "q", "search", "title", "role", "persona"].find(
|
|
6289
|
+
(field) => includeOptional.includes(field)
|
|
6290
|
+
) ?? "query" : null;
|
|
6291
|
+
if (activeTodoField) {
|
|
6292
|
+
lines.push(
|
|
6293
|
+
`${indent}// ${activeTodoField}: ${exampleValueComment(activeTodoField)},`
|
|
6294
|
+
);
|
|
6295
|
+
}
|
|
6296
|
+
const optionalExamples = includeOptional.filter(
|
|
6297
|
+
(field) => !required.includes(field) && field !== activeTodoField
|
|
6298
|
+
);
|
|
6299
|
+
if (optionalExamples.length > 0) {
|
|
6300
|
+
lines.push("");
|
|
6301
|
+
lines.push(`${indent}// optional - uncomment what this provider supports:`);
|
|
6302
|
+
for (const field of optionalExamples) {
|
|
6303
|
+
if (field === "limit" || field === "numResults" || field === "num_results" || field === "page_size") {
|
|
6304
|
+
lines.push(`${indent}// ${field}: limit,`);
|
|
6305
|
+
} else {
|
|
6306
|
+
lines.push(`${indent}// ${field}: ${exampleValueComment(field)},`);
|
|
6307
|
+
}
|
|
6308
|
+
}
|
|
6309
|
+
}
|
|
6310
|
+
if (!required.some(
|
|
6311
|
+
(field) => ["limit", "numResults", "num_results", "page_size"].includes(field)
|
|
6312
|
+
)) {
|
|
6313
|
+
lines.push(`${indent}limit,`);
|
|
6314
|
+
}
|
|
6315
|
+
return `{
|
|
6316
|
+
${lines.join("\n")}
|
|
6317
|
+
${indent.slice(2)}}`;
|
|
6318
|
+
}
|
|
6319
|
+
function generatePlayInputObject(input) {
|
|
6320
|
+
const { schema, indent, label, entity } = input;
|
|
6321
|
+
const details = schemaFieldDetails(schema);
|
|
6322
|
+
const fallback = entity === "company" ? ["domain", "company_name"] : ["first_name", "last_name", "domain"];
|
|
6323
|
+
const required = details.required.length ? details.required : fallback;
|
|
6324
|
+
const lines = [
|
|
6325
|
+
`${indent}// TODO: fill source play inputs for ${label}.`,
|
|
6326
|
+
...playInspectionComments(label, indent)
|
|
6327
|
+
];
|
|
6328
|
+
for (const field of required) {
|
|
6329
|
+
lines.push(`${indent}// ${field}: ${exampleValueComment(field)},`);
|
|
6330
|
+
}
|
|
6331
|
+
if (!required.includes("limit")) lines.push(`${indent}limit,`);
|
|
6332
|
+
return `{
|
|
6333
|
+
${lines.join("\n")}
|
|
6334
|
+
${indent.slice(2)}}`;
|
|
6335
|
+
}
|
|
6336
|
+
function requiredPlayInputFields(play) {
|
|
6337
|
+
const schema = play?.inputSchema;
|
|
6338
|
+
if (!isRecord3(schema)) return [];
|
|
6339
|
+
if (Array.isArray(schema.required)) {
|
|
6340
|
+
return schema.required.filter(
|
|
6341
|
+
(value) => typeof value === "string"
|
|
6342
|
+
);
|
|
6343
|
+
}
|
|
6344
|
+
if (Array.isArray(schema.fields)) {
|
|
6345
|
+
return schema.fields.filter(isRecord3).filter(
|
|
6346
|
+
(field) => field.required === true && typeof field.name === "string"
|
|
6347
|
+
).map((field) => String(field.name));
|
|
6348
|
+
}
|
|
6349
|
+
return [];
|
|
6350
|
+
}
|
|
6351
|
+
function sourceProviders(options) {
|
|
6352
|
+
return options.from.kind === "providers" ? options.from.values : [];
|
|
6353
|
+
}
|
|
6354
|
+
function sourcePlayRef(options) {
|
|
6355
|
+
return options.from.kind === "play" ? options.from.value : null;
|
|
6356
|
+
}
|
|
6357
|
+
function stagePlayRef(stage) {
|
|
6358
|
+
return stage?.kind === "play" ? stage.value : null;
|
|
6359
|
+
}
|
|
6360
|
+
function stageProviders(stage) {
|
|
6361
|
+
return stage?.kind === "providers" ? stage.values : [];
|
|
6362
|
+
}
|
|
6363
|
+
function finderStage(options, finder) {
|
|
6364
|
+
return finder === "email_finder" ? options.email : options.phone;
|
|
6365
|
+
}
|
|
6366
|
+
function stepFieldName(finder) {
|
|
6367
|
+
return PLAY_BOOTSTRAP_OUTPUT_FIELD_BY_FINDER[finder];
|
|
6368
|
+
}
|
|
6369
|
+
function finderProviderStepPrefix(finder) {
|
|
6370
|
+
return finder === "email_finder" ? "emailFinder" : "phoneFinder";
|
|
6371
|
+
}
|
|
6372
|
+
function safeIdentifier(value) {
|
|
6373
|
+
return value.replace(/[^A-Za-z0-9_]+/g, "_");
|
|
6374
|
+
}
|
|
6375
|
+
function playInspectionComments(playRef, indent) {
|
|
6376
|
+
return [`${indent}// Inspect: deepline plays describe ${playRef} --json`];
|
|
6377
|
+
}
|
|
6378
|
+
function accessorExpression(base, field) {
|
|
6379
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(field) ? `${base}.${field}` : `${base}[${jsString(field)}]`;
|
|
6380
|
+
}
|
|
6381
|
+
function needsWhenImport(options) {
|
|
6382
|
+
return stageProviders(options.email).length > 1 || stageProviders(options.phone).length > 1;
|
|
6383
|
+
}
|
|
6384
|
+
function sourceCollectionTypeName(entity) {
|
|
6385
|
+
return entity === "company" ? "CompanySourceRow" : "ContactSourceRow";
|
|
6386
|
+
}
|
|
6387
|
+
function renderPartialRowType(input) {
|
|
6388
|
+
if (input.fields.length === 0) {
|
|
6389
|
+
return `type ${input.typeName} = Record<string, unknown>;`;
|
|
6390
|
+
}
|
|
6391
|
+
const properties = input.fields.map((field) => ` ${jsString(field)}?: unknown;`).join("\n");
|
|
6392
|
+
return `type ${input.typeName} = Record<string, unknown> & Partial<{
|
|
6393
|
+
// ${input.comment}
|
|
6394
|
+
${properties}
|
|
6395
|
+
}>;`;
|
|
6396
|
+
}
|
|
6397
|
+
function fieldsFromSchemaDetails(input) {
|
|
6398
|
+
return [
|
|
6399
|
+
...input.details.required.length ? input.details.required : input.fallbackFields,
|
|
6400
|
+
...input.details.optional
|
|
6401
|
+
];
|
|
6402
|
+
}
|
|
6403
|
+
function schemaFieldsForStage(stage, input) {
|
|
6404
|
+
switch (stage?.kind) {
|
|
6405
|
+
case void 0:
|
|
6406
|
+
return [];
|
|
6407
|
+
case "play":
|
|
6408
|
+
return fieldsFromSchemaDetails({
|
|
6409
|
+
details: schemaFieldDetails(input.play?.inputSchema),
|
|
6410
|
+
fallbackFields: input.fallbackFields
|
|
6411
|
+
});
|
|
6412
|
+
case "providers":
|
|
6413
|
+
return input.tools.flatMap(
|
|
6414
|
+
(tool) => fieldsFromSchemaDetails({
|
|
6415
|
+
details: schemaFieldDetails(tool.inputSchema),
|
|
6416
|
+
fallbackFields: input.fallbackFields
|
|
6417
|
+
})
|
|
6418
|
+
);
|
|
6419
|
+
}
|
|
6420
|
+
}
|
|
6421
|
+
function sourceRowTypeDefinition(input) {
|
|
6422
|
+
switch (input.options.from.kind) {
|
|
6423
|
+
case "csv":
|
|
6424
|
+
return `type ${input.sourceTypeName} = SourceCsvRow;`;
|
|
6425
|
+
case "providers":
|
|
6426
|
+
return renderPartialRowType({
|
|
6427
|
+
typeName: input.sourceTypeName,
|
|
6428
|
+
fields: [
|
|
6429
|
+
...new Set(input.sourceTools.flatMap(listRowCandidateKeysFromTool))
|
|
6430
|
+
].sort(),
|
|
6431
|
+
comment: "Candidate source row keys from described list getters; confirm the actual provider keys with source_*.extractedLists.*.keys before mapping."
|
|
6432
|
+
});
|
|
6433
|
+
case "play": {
|
|
6434
|
+
const details = schemaFieldDetails(input.sourcePlay?.outputSchema);
|
|
6435
|
+
return renderPartialRowType({
|
|
6436
|
+
typeName: input.sourceTypeName,
|
|
6437
|
+
fields: [...details.required, ...details.optional].sort(),
|
|
6438
|
+
comment: "Candidate source play output fields; confirm the selected rows field before mapping."
|
|
6439
|
+
});
|
|
6440
|
+
}
|
|
6441
|
+
}
|
|
6442
|
+
}
|
|
6443
|
+
function contactBridgeRowTypeDefinition(input) {
|
|
6444
|
+
const config = playBootstrapTemplateConfig(input.options.template);
|
|
6445
|
+
if (config.sourceEntity !== "company" || !input.options.people) return null;
|
|
6446
|
+
const emailFields = schemaFieldsForStage(input.options.email, {
|
|
6447
|
+
tools: input.finderTools.email_finder ?? [],
|
|
6448
|
+
play: input.finderPlays.email_finder,
|
|
6449
|
+
fallbackFields: ["first_name", "last_name", "domain"]
|
|
6450
|
+
});
|
|
6451
|
+
const phoneFields = schemaFieldsForStage(input.options.phone, {
|
|
6452
|
+
tools: input.finderTools.phone_finder ?? [],
|
|
6453
|
+
play: input.finderPlays.phone_finder,
|
|
6454
|
+
fallbackFields: ["first_name", "last_name", "domain"]
|
|
6455
|
+
});
|
|
6456
|
+
return renderPartialRowType({
|
|
6457
|
+
typeName: "ContactSourceRow",
|
|
6458
|
+
fields: [
|
|
6459
|
+
.../* @__PURE__ */ new Set([
|
|
6460
|
+
...inputPropertyNames(input.peoplePlay?.outputSchema),
|
|
6461
|
+
...emailFields,
|
|
6462
|
+
...phoneFields
|
|
6463
|
+
])
|
|
6464
|
+
].sort(),
|
|
6465
|
+
comment: "Fields the people play or later finder stages may need; the generated code still requires explicit mapping."
|
|
6466
|
+
});
|
|
6467
|
+
}
|
|
6468
|
+
function generateRowTypeDefinitions(input) {
|
|
6469
|
+
const config = playBootstrapTemplateConfig(input.options.template);
|
|
6470
|
+
const sourceTypeName = sourceCollectionTypeName(config.sourceEntity);
|
|
6471
|
+
const definitions = [
|
|
6472
|
+
sourceRowTypeDefinition({
|
|
6473
|
+
options: input.options,
|
|
6474
|
+
sourceTypeName,
|
|
6475
|
+
sourceTools: input.sourceTools,
|
|
6476
|
+
sourcePlay: input.sourcePlay
|
|
6477
|
+
}),
|
|
6478
|
+
contactBridgeRowTypeDefinition(input)
|
|
6479
|
+
];
|
|
6480
|
+
return definitions.filter(Boolean).join("\n\n");
|
|
6481
|
+
}
|
|
6482
|
+
function validateBootstrapRoutes(input) {
|
|
6483
|
+
const config = playBootstrapTemplateConfig(input.options.template);
|
|
6484
|
+
const sourceCategory = config.sourceEntity === "company" ? PLAY_BOOTSTRAP_COMPANY_PROVIDER_CATEGORY : PLAY_BOOTSTRAP_PEOPLE_PROVIDER_CATEGORY;
|
|
6485
|
+
for (const tool of input.sourceTools) {
|
|
6486
|
+
if (!tool.categories.includes(sourceCategory)) {
|
|
6487
|
+
throw new PlayBootstrapValidationError(
|
|
6488
|
+
`Cannot use ${tool.toolId} as a ${config.sourceEntity} source for ${input.options.template}: expected category ${sourceCategory}, got ${tool.categories.join(", ") || "none"}. Run: deepline tools grep ${sourceCategory} --categories ${sourceCategory} --json`
|
|
6489
|
+
);
|
|
6490
|
+
}
|
|
6491
|
+
if (getterNamesFromTool(tool, "list").length === 0) {
|
|
6492
|
+
throw new PlayBootstrapValidationError(
|
|
6493
|
+
`Cannot use ${tool.toolId} as a ${config.sourceEntity} source: it exposes no extracted list getters. Run: deepline tools describe ${tool.toolId} --json`
|
|
6494
|
+
);
|
|
6495
|
+
}
|
|
6496
|
+
}
|
|
6497
|
+
if (input.options.people?.kind === "providers") {
|
|
6498
|
+
throw new PlayBootstrapValidationError(
|
|
6499
|
+
"Company-to-people bootstrap only accepts --people play:<play-ref> for now. Providers are too task-specific; choose or create a people play so the generated file can show the mapping contract."
|
|
6500
|
+
);
|
|
6501
|
+
}
|
|
6502
|
+
for (const finder of ["email_finder", "phone_finder"]) {
|
|
6503
|
+
const requiredCategory = PLAY_BOOTSTRAP_PROVIDER_CATEGORY_BY_FINDER[finder];
|
|
6504
|
+
for (const tool of input.finderTools[finder] ?? []) {
|
|
6505
|
+
if (!tool.categories.includes(requiredCategory)) {
|
|
6506
|
+
throw new PlayBootstrapValidationError(
|
|
6507
|
+
`Cannot use ${tool.toolId} for ${finder}: expected category ${requiredCategory}, got ${tool.categories.join(", ") || "none"}. Run: deepline tools grep ${requiredCategory} --categories ${requiredCategory} --json`
|
|
6508
|
+
);
|
|
6509
|
+
}
|
|
6510
|
+
if (getterNamesFromTool(tool, "value").length === 0) {
|
|
6511
|
+
throw new PlayBootstrapValidationError(
|
|
6512
|
+
`Cannot use ${tool.toolId} as a ${finder}: it exposes no extracted value getters. Run: deepline tools describe ${tool.toolId} --json`
|
|
6513
|
+
);
|
|
6514
|
+
}
|
|
6515
|
+
}
|
|
6516
|
+
}
|
|
6517
|
+
}
|
|
6518
|
+
function sourceCollectionName(entity) {
|
|
6519
|
+
switch (entity) {
|
|
6520
|
+
case "company":
|
|
6521
|
+
return "companies";
|
|
6522
|
+
case "people":
|
|
6523
|
+
return "contacts";
|
|
6524
|
+
}
|
|
6525
|
+
}
|
|
6526
|
+
function requiredGetterName(input) {
|
|
6527
|
+
const getter = getterNamesFromTool(input.tool, input.kind)[0];
|
|
6528
|
+
if (!getter) {
|
|
6529
|
+
switch (input.kind) {
|
|
6530
|
+
case "list":
|
|
6531
|
+
throw new PlayBootstrapValidationError(
|
|
6532
|
+
`Cannot use ${input.label} as a source: it exposes no extracted list getters.`
|
|
6533
|
+
);
|
|
6534
|
+
case "value":
|
|
6535
|
+
throw new PlayBootstrapValidationError(
|
|
6536
|
+
`Cannot use ${input.label} as a finder: it exposes no extracted value getters.`
|
|
6537
|
+
);
|
|
6538
|
+
}
|
|
6539
|
+
}
|
|
6540
|
+
return getter;
|
|
6541
|
+
}
|
|
6542
|
+
function generateCsvSourceRowsBlock(input) {
|
|
6543
|
+
return `const sourceDataset = await ctx.csv<SourceCsvRow>(${jsString(input.packagedSourceCsvPath ?? input.source.value)});
|
|
6544
|
+
const ${input.collection}: ${input.collectionType}[] = await sourceDataset.peek(limit);`;
|
|
6545
|
+
}
|
|
6546
|
+
function generatePlaySourceRowsBlock(input) {
|
|
6547
|
+
const playInput = generatePlayInputObject({
|
|
6548
|
+
schema: input.sourcePlay?.inputSchema,
|
|
6549
|
+
indent: " ",
|
|
6550
|
+
label: input.source.value,
|
|
6551
|
+
entity: input.entity
|
|
6552
|
+
});
|
|
6553
|
+
return `const sourceInput = ${playInput};
|
|
6554
|
+
throw new Error(${jsString(`TODO: map sourceInput for ${input.source.value}, choose the play output rows field, then delete this throw.`)});
|
|
6555
|
+
const sourceResult = await ctx.runPlay('source_play', ${jsString(input.source.value)}, sourceInput, {
|
|
6556
|
+
description: ${jsString(`Seed ${input.entity} rows from the selected play.`)},
|
|
6557
|
+
});
|
|
6558
|
+
// TODO: Replace sourceResult.rows with the selected play's actual row output field.
|
|
6559
|
+
const ${input.collection}: ${input.collectionType}[] = (sourceResult.rows ?? []) as ${input.collectionType}[];`;
|
|
6560
|
+
}
|
|
6561
|
+
function generateProviderSourceBlock(input) {
|
|
6562
|
+
const getter = requiredGetterName({
|
|
6563
|
+
tool: input.tool,
|
|
6564
|
+
kind: "list",
|
|
6565
|
+
label: input.provider
|
|
6566
|
+
});
|
|
6567
|
+
const inputName = `${input.entity}Input_${input.index}`;
|
|
6568
|
+
return `// ${input.entity === "company" ? "Company" : "People"} source provider: ${input.provider}
|
|
6569
|
+
const ${inputName}: Record<string, unknown> = ${generateSourceProviderInputObject(
|
|
6570
|
+
{
|
|
6571
|
+
tool: input.tool,
|
|
6572
|
+
indent: " ",
|
|
6573
|
+
label: input.provider,
|
|
6574
|
+
entity: input.entity
|
|
6575
|
+
}
|
|
6576
|
+
)};
|
|
6577
|
+
throw new Error(${jsString(`TODO: fill ${inputName} for ${input.provider}, then delete this throw.`)});
|
|
6578
|
+
const source_${input.index} = await ctx.tools.execute({
|
|
6579
|
+
id: ${jsString(`${input.entity}_source_${input.index}`)},
|
|
6580
|
+
tool: ${jsString(input.provider)},
|
|
6581
|
+
input: ${inputName},
|
|
6582
|
+
description: ${jsString(`Seed ${input.entity} rows from ${input.provider}.`)},
|
|
6583
|
+
});
|
|
6584
|
+
// extractedLists.${getter}.get() returns provider-shaped rows. Do not assume canonical fields;
|
|
6585
|
+
// inspect source_${input.index}.extractedLists.${getter}.keys or the provider output schema, then map explicitly below.
|
|
6586
|
+
const sourceRows_${input.index} = ${accessorExpression(`source_${input.index}.extractedLists`, getter)}.get() as ${input.collectionType}[];`;
|
|
6587
|
+
}
|
|
6588
|
+
function generateProviderSourceRowsBlock(input) {
|
|
6589
|
+
const blocks = input.source.values.map(
|
|
6590
|
+
(provider, index) => generateProviderSourceBlock({
|
|
6591
|
+
provider,
|
|
6592
|
+
index,
|
|
6593
|
+
tool: input.sourceTools[index] ?? null,
|
|
6594
|
+
entity: input.entity,
|
|
6595
|
+
collectionType: input.collectionType
|
|
6596
|
+
})
|
|
6597
|
+
);
|
|
6598
|
+
return `${blocks.join("\n\n ")}
|
|
6599
|
+
const ${input.collection}: ${input.collectionType}[] = [${input.source.values.map((_, index) => `...sourceRows_${index}`).join(", ")}];`;
|
|
6600
|
+
}
|
|
6601
|
+
function generateSourceRowsBlock(input) {
|
|
6602
|
+
const config = playBootstrapTemplateConfig(input.options.template);
|
|
6603
|
+
const entity = config.sourceEntity;
|
|
6604
|
+
const collection = sourceCollectionName(entity);
|
|
6605
|
+
const collectionType = sourceCollectionTypeName(entity);
|
|
6606
|
+
switch (input.options.from.kind) {
|
|
6607
|
+
case "csv":
|
|
6608
|
+
return generateCsvSourceRowsBlock({
|
|
6609
|
+
source: input.options.from,
|
|
6610
|
+
collection,
|
|
6611
|
+
collectionType,
|
|
6612
|
+
packagedSourceCsvPath: input.packagedSourceCsvPath
|
|
6613
|
+
});
|
|
6614
|
+
case "play":
|
|
6615
|
+
return generatePlaySourceRowsBlock({
|
|
6616
|
+
source: input.options.from,
|
|
6617
|
+
sourcePlay: input.sourcePlay,
|
|
6618
|
+
entity,
|
|
6619
|
+
collection,
|
|
6620
|
+
collectionType
|
|
6621
|
+
});
|
|
6622
|
+
case "providers":
|
|
6623
|
+
return generateProviderSourceRowsBlock({
|
|
6624
|
+
source: input.options.from,
|
|
6625
|
+
sourceTools: input.sourceTools,
|
|
6626
|
+
entity,
|
|
6627
|
+
collection,
|
|
6628
|
+
collectionType
|
|
6629
|
+
});
|
|
6630
|
+
}
|
|
6631
|
+
}
|
|
6632
|
+
function generateSourceSeedBlock(input) {
|
|
6633
|
+
const sourceRows = generateSourceRowsBlock(input);
|
|
6634
|
+
const peoplePlayRef = stagePlayRef(input.options.people);
|
|
6635
|
+
if (!peoplePlayRef) return sourceRows;
|
|
6636
|
+
const peopleInput = generateCompanyInputObjectFromSchema(
|
|
6637
|
+
input.peoplePlay?.inputSchema,
|
|
6638
|
+
" ",
|
|
6639
|
+
peoplePlayRef,
|
|
6640
|
+
["domain", "company_name"]
|
|
6641
|
+
);
|
|
6642
|
+
return `${sourceRows}
|
|
6643
|
+
const contacts: ContactSourceRow[] = [];
|
|
6644
|
+
for (const [index, company] of companies.slice(0, limit).entries()) {
|
|
6645
|
+
const peopleInput = ${peopleInput};
|
|
6646
|
+
throw new Error(${jsString(`TODO: map company fields into peopleInput for ${peoplePlayRef}, choose the play output rows field, then delete this throw.`)});
|
|
6647
|
+
const peopleResult = await ctx.runPlay(
|
|
6648
|
+
\`people_play_\${index}\`,
|
|
6649
|
+
${jsString(peoplePlayRef)},
|
|
6650
|
+
peopleInput,
|
|
6651
|
+
{
|
|
6652
|
+
description: 'Map one company row into people/contact rows with the selected play.',
|
|
6653
|
+
},
|
|
6654
|
+
);
|
|
6655
|
+
// TODO: Replace peopleResult.rows with the selected play's actual contact rows field.
|
|
6656
|
+
contacts.push(...((peopleResult.rows ?? []) as ContactSourceRow[]));
|
|
6657
|
+
}`;
|
|
6658
|
+
}
|
|
6659
|
+
function finderProviderStepName(input) {
|
|
6660
|
+
return `${input.aggregateStepName}${input.index}_${safeIdentifier(input.provider)}`;
|
|
6661
|
+
}
|
|
6662
|
+
function finderValueGetter(input) {
|
|
6663
|
+
const getters = getterNamesFromTool(input.tool, "value");
|
|
6664
|
+
const getter = getters.find((name) => name === input.outputField) ?? requiredGetterName({
|
|
6665
|
+
tool: input.tool,
|
|
6666
|
+
kind: "value",
|
|
6667
|
+
label: input.provider
|
|
6668
|
+
});
|
|
6669
|
+
return getter;
|
|
6670
|
+
}
|
|
6671
|
+
function optionalFinderValueExpression(candidateExpression, outputField) {
|
|
6672
|
+
const valueExpression = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(outputField) ? `${candidateExpression}?.${outputField}` : `${candidateExpression}?.[${jsString(outputField)}]`;
|
|
6673
|
+
return `${valueExpression}?.trim()`;
|
|
6674
|
+
}
|
|
6675
|
+
function generateFinderPlayStep(input) {
|
|
6676
|
+
const outputField = PLAY_BOOTSTRAP_OUTPUT_FIELD_BY_FINDER[input.finder];
|
|
6677
|
+
const resultTypeName = finderResultTypeName(input.finder);
|
|
6678
|
+
const payload = generateContactInputObjectFromSchema(
|
|
6679
|
+
input.play?.inputSchema,
|
|
6680
|
+
" ",
|
|
6681
|
+
input.stage.value
|
|
6682
|
+
);
|
|
6683
|
+
return `.step(${jsString(outputField)}, async (row, rowCtx) => {
|
|
6684
|
+
const ${input.aggregateStepName}Input = ${payload};
|
|
6685
|
+
throw new Error(${jsString(`TODO: map ${input.aggregateStepName}Input for ${input.stage.value}, then delete this throw.`)});
|
|
6686
|
+
const ${input.aggregateStepName}Result = await rowCtx.runPlay<${resultTypeName}>(
|
|
6687
|
+
${jsString(`${input.aggregateStepName}Play`)},
|
|
6688
|
+
${jsString(input.stage.value)},
|
|
6689
|
+
${input.aggregateStepName}Input,
|
|
6690
|
+
{
|
|
6691
|
+
description: ${jsString(`Run ${input.finder} play.`)},
|
|
6692
|
+
},
|
|
6693
|
+
);
|
|
6694
|
+
return typeof ${input.aggregateStepName}Result === 'string'
|
|
6695
|
+
? ${input.aggregateStepName}Result.trim() || null
|
|
6696
|
+
: ${input.aggregateStepName}Result?.${outputField} ?? null;
|
|
6697
|
+
})`;
|
|
6698
|
+
}
|
|
6699
|
+
function generateFinderProviderResolver(input) {
|
|
6700
|
+
const payload = generateContactInputObjectFromSchema(
|
|
6701
|
+
input.tool?.inputSchema,
|
|
6702
|
+
" ",
|
|
6703
|
+
input.provider
|
|
6704
|
+
);
|
|
6705
|
+
const getter = finderValueGetter({
|
|
6706
|
+
tool: input.tool,
|
|
6707
|
+
provider: input.provider,
|
|
6708
|
+
outputField: input.outputField
|
|
6709
|
+
});
|
|
6710
|
+
return `async (row, rowCtx) => {
|
|
6711
|
+
const providerInput = ${payload};
|
|
6712
|
+
throw new Error(${jsString(`TODO: map providerInput for ${input.provider}, then delete this throw.`)});
|
|
6713
|
+
const result = await rowCtx.tools.execute({
|
|
6714
|
+
id: ${jsString(`${input.aggregateStepName}_${input.providerIndex}`)},
|
|
6715
|
+
tool: ${jsString(input.provider)},
|
|
6716
|
+
input: providerInput,
|
|
6717
|
+
description: ${jsString(`Try ${input.provider} as a ${input.finder}.`)},
|
|
6718
|
+
});
|
|
6719
|
+
return {
|
|
6720
|
+
${input.outputField}: (${accessorExpression("result.extractedValues", getter)}.get() as string | null) ?? null,
|
|
6721
|
+
result,
|
|
6722
|
+
};
|
|
6723
|
+
}`;
|
|
6724
|
+
}
|
|
6725
|
+
function generateFinderProviderStep(input) {
|
|
6726
|
+
const stepName = input.stepNames[input.index];
|
|
6727
|
+
switch (input.index) {
|
|
6728
|
+
case 0:
|
|
6729
|
+
return `.step(${jsString(stepName)}, ${input.resolver})`;
|
|
6730
|
+
default: {
|
|
6731
|
+
const priorCandidates = input.stepNames.slice(0, input.index).map((name) => `row.${name}`).join(", ");
|
|
6732
|
+
return `.step(
|
|
6733
|
+
${jsString(stepName)},
|
|
6734
|
+
when(
|
|
6735
|
+
(row) => ![${priorCandidates}].some((candidate) => ${optionalFinderValueExpression("candidate", input.outputField)}),
|
|
6736
|
+
${input.resolver},
|
|
6737
|
+
),
|
|
6738
|
+
)`;
|
|
6739
|
+
}
|
|
6740
|
+
}
|
|
6741
|
+
}
|
|
6742
|
+
function generateFinderProviderWaterfall(input) {
|
|
6743
|
+
const legPrefix = finderProviderStepPrefix(input.finder);
|
|
6744
|
+
const stepNames = input.stage.values.map(
|
|
6745
|
+
(provider, index) => finderProviderStepName({
|
|
6746
|
+
aggregateStepName: legPrefix,
|
|
6747
|
+
provider,
|
|
6748
|
+
index
|
|
6749
|
+
})
|
|
6750
|
+
);
|
|
6751
|
+
const providerSteps = input.stage.values.map(
|
|
6752
|
+
(provider, index) => generateFinderProviderStep({
|
|
6753
|
+
index,
|
|
6754
|
+
stepNames,
|
|
6755
|
+
outputField: input.outputField,
|
|
6756
|
+
resolver: generateFinderProviderResolver({
|
|
6757
|
+
finder: input.finder,
|
|
6758
|
+
provider,
|
|
6759
|
+
providerIndex: index,
|
|
6760
|
+
tool: input.tools[index] ?? null,
|
|
6761
|
+
aggregateStepName: input.aggregateStepName,
|
|
6762
|
+
outputField: input.outputField
|
|
6763
|
+
})
|
|
6764
|
+
})
|
|
5555
6765
|
);
|
|
6766
|
+
const candidateNames = stepNames.map((name) => `row.${name}`).join(", ");
|
|
6767
|
+
return `// ${input.aggregateStepName} provider waterfall. Each provider leg is active once its TODO throw is removed;
|
|
6768
|
+
// delete or comment out legs you do not want before running. Later legs are gated with when(...).
|
|
6769
|
+
${providerSteps.join("\n ")}
|
|
6770
|
+
.step(${jsString(input.aggregateStepName)}, (row) => {
|
|
6771
|
+
const candidates = [${candidateNames}];
|
|
6772
|
+
const match = candidates.find((candidate) => ${optionalFinderValueExpression("candidate", input.outputField)});
|
|
6773
|
+
return ${optionalFinderValueExpression("match", input.outputField)} ?? null;
|
|
6774
|
+
})`;
|
|
6775
|
+
}
|
|
6776
|
+
function generateFinderStageSteps(input) {
|
|
6777
|
+
const outputField = PLAY_BOOTSTRAP_OUTPUT_FIELD_BY_FINDER[input.finder];
|
|
6778
|
+
const aggregateStepName = stepFieldName(input.finder);
|
|
6779
|
+
switch (input.stage.kind) {
|
|
6780
|
+
case "play":
|
|
6781
|
+
return generateFinderPlayStep({
|
|
6782
|
+
finder: input.finder,
|
|
6783
|
+
stage: input.stage,
|
|
6784
|
+
play: input.finderPlays[input.finder],
|
|
6785
|
+
aggregateStepName
|
|
6786
|
+
});
|
|
6787
|
+
case "providers":
|
|
6788
|
+
return generateFinderProviderWaterfall({
|
|
6789
|
+
finder: input.finder,
|
|
6790
|
+
stage: input.stage,
|
|
6791
|
+
tools: input.finderTools[input.finder] ?? [],
|
|
6792
|
+
aggregateStepName,
|
|
6793
|
+
outputField
|
|
6794
|
+
});
|
|
6795
|
+
}
|
|
5556
6796
|
}
|
|
5557
|
-
function
|
|
5558
|
-
return
|
|
5559
|
-
|
|
5560
|
-
|
|
6797
|
+
function generateFinderSteps(input) {
|
|
6798
|
+
return ["email_finder", "phone_finder"].flatMap((finder) => {
|
|
6799
|
+
const stage = finderStage(input.options, finder);
|
|
6800
|
+
return stage ? [
|
|
6801
|
+
generateFinderStageSteps({
|
|
6802
|
+
...input,
|
|
6803
|
+
finder,
|
|
6804
|
+
stage
|
|
6805
|
+
})
|
|
6806
|
+
] : [];
|
|
6807
|
+
}).join("\n ");
|
|
6808
|
+
}
|
|
6809
|
+
function generateBootstrapPlaySource(input) {
|
|
6810
|
+
const config = playBootstrapTemplateConfig(input.options.template);
|
|
6811
|
+
const sourceSeedBlock = generateSourceSeedBlock(input);
|
|
6812
|
+
const finderSteps = generateFinderSteps(input);
|
|
6813
|
+
const hasPeople = config.sourceEntity === "people" || Boolean(input.options.people);
|
|
6814
|
+
const sourceCollection = hasPeople ? "contacts" : "companies";
|
|
6815
|
+
const mapSteps = finderSteps ? `
|
|
6816
|
+
${finderSteps}` : "";
|
|
6817
|
+
const sourceCsvRowType = input.options.from.kind === "csv" ? renderSourceCsvRowType(input.sourceCsvColumns) : "";
|
|
6818
|
+
const rowTypeDefinitions = generateRowTypeDefinitions(input);
|
|
6819
|
+
const finderPlayResultTypes = generatedFinderPlayResultTypes(input);
|
|
6820
|
+
const typeDefinitions = [
|
|
6821
|
+
sourceCsvRowType.trimEnd(),
|
|
6822
|
+
rowTypeDefinitions,
|
|
6823
|
+
finderPlayResultTypes
|
|
6824
|
+
].filter((definition) => definition.trim().length > 0).join("\n\n");
|
|
6825
|
+
const importNames = needsWhenImport(input.options) ? "definePlay, when" : "definePlay";
|
|
6826
|
+
return `import { ${importNames} } from 'deepline';
|
|
6827
|
+
|
|
6828
|
+
${typeDefinitions}
|
|
6829
|
+
|
|
6830
|
+
type Input = {
|
|
6831
|
+
limit?: number;
|
|
6832
|
+
};
|
|
6833
|
+
|
|
6834
|
+
export default definePlay(${jsString(input.options.name)}, async (ctx, input: Input = {}) => {
|
|
6835
|
+
const limit = Math.max(1, Math.min(Number(input.limit ?? ${input.options.limit}), ${input.options.limit}));
|
|
6836
|
+
${sourceSeedBlock}
|
|
6837
|
+
|
|
6838
|
+
const rowsToProcess = ${sourceCollection}.slice(0, limit);
|
|
6839
|
+
if (rowsToProcess.length === 0) {
|
|
6840
|
+
throw new Error('plays bootstrap found 0 source rows. Check the source provider/play/CSV output.');
|
|
6841
|
+
}
|
|
6842
|
+
|
|
6843
|
+
const rows = await ctx
|
|
6844
|
+
.map('bootstrap_rows', rowsToProcess)${mapSteps}
|
|
6845
|
+
.run({
|
|
6846
|
+
key: (_row, index) => index,
|
|
6847
|
+
description: ${jsString(`Bootstrap ${input.options.template}: seed source rows, run requested stages, then return the mapped rows.`)},
|
|
6848
|
+
});
|
|
6849
|
+
|
|
5561
6850
|
return {
|
|
5562
|
-
|
|
5563
|
-
|
|
5564
|
-
cacheDir: join5(
|
|
5565
|
-
tmpdir2(),
|
|
5566
|
-
`deepline-play-artifacts-v${PLAY_BUNDLE_CACHE_VERSION2}`
|
|
5567
|
-
),
|
|
5568
|
-
sdkSourceRoot: SDK_SOURCE_ROOT,
|
|
5569
|
-
sdkPackageJson: SDK_PACKAGE_JSON,
|
|
5570
|
-
sdkEntryFile: SDK_ENTRY_FILE,
|
|
5571
|
-
sdkTypesEntryFile: HAS_SOURCE_BUNDLING_SOURCES || !existsSync5(SDK_TYPES_ENTRY_FILE) ? SDK_ENTRY_FILE : SDK_TYPES_ENTRY_FILE,
|
|
5572
|
-
sdkWorkersEntryFile: SDK_WORKERS_ENTRY_FILE,
|
|
5573
|
-
workersHarnessEntryFile: WORKERS_HARNESS_ENTRY_FILE,
|
|
5574
|
-
workersHarnessFilesDir: WORKERS_HARNESS_FILES_DIR,
|
|
5575
|
-
discoverPackagedLocalFiles,
|
|
5576
|
-
warnAboutNonDevelopmentBundling
|
|
6851
|
+
count: await rows.count(),
|
|
6852
|
+
rows,
|
|
5577
6853
|
};
|
|
6854
|
+
});
|
|
6855
|
+
|
|
6856
|
+
`;
|
|
5578
6857
|
}
|
|
5579
|
-
async function
|
|
5580
|
-
return
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
6858
|
+
async function describePlayMaybe(client, playRef) {
|
|
6859
|
+
return playRef ? client.describePlay(playRef, { compact: true }) : null;
|
|
6860
|
+
}
|
|
6861
|
+
function loadTools(client, providers) {
|
|
6862
|
+
return Promise.all(providers.map((provider) => client.getTool(provider)));
|
|
6863
|
+
}
|
|
6864
|
+
async function loadBootstrapContracts(client, options) {
|
|
6865
|
+
const [
|
|
6866
|
+
sourceTools,
|
|
6867
|
+
sourcePlay,
|
|
6868
|
+
peoplePlay,
|
|
6869
|
+
emailPlay,
|
|
6870
|
+
phonePlay,
|
|
6871
|
+
emailTools,
|
|
6872
|
+
phoneTools
|
|
6873
|
+
] = await Promise.all([
|
|
6874
|
+
loadTools(client, sourceProviders(options)),
|
|
6875
|
+
describePlayMaybe(client, sourcePlayRef(options)),
|
|
6876
|
+
describePlayMaybe(client, stagePlayRef(options.people)),
|
|
6877
|
+
describePlayMaybe(client, stagePlayRef(options.email)),
|
|
6878
|
+
describePlayMaybe(client, stagePlayRef(options.phone)),
|
|
6879
|
+
loadTools(client, stageProviders(options.email)),
|
|
6880
|
+
loadTools(client, stageProviders(options.phone))
|
|
6881
|
+
]);
|
|
6882
|
+
const contracts = {
|
|
6883
|
+
sourceTools,
|
|
6884
|
+
sourcePlay,
|
|
6885
|
+
peoplePlay,
|
|
6886
|
+
finderTools: {
|
|
6887
|
+
email_finder: emailTools,
|
|
6888
|
+
phone_finder: phoneTools
|
|
6889
|
+
},
|
|
6890
|
+
finderPlays: {
|
|
6891
|
+
email_finder: emailPlay,
|
|
6892
|
+
phone_finder: phonePlay
|
|
6893
|
+
}
|
|
6894
|
+
};
|
|
6895
|
+
validateBootstrapRoutes({
|
|
6896
|
+
options,
|
|
6897
|
+
sourceTools: contracts.sourceTools,
|
|
6898
|
+
peoplePlay: contracts.peoplePlay,
|
|
6899
|
+
finderTools: contracts.finderTools,
|
|
6900
|
+
finderPlays: contracts.finderPlays
|
|
6901
|
+
});
|
|
6902
|
+
return contracts;
|
|
6903
|
+
}
|
|
6904
|
+
function loadCsvContext(source) {
|
|
6905
|
+
switch (source.kind) {
|
|
6906
|
+
case "csv":
|
|
6907
|
+
return {
|
|
6908
|
+
packagedSourceCsvPath: packagedCsvPathForPlay(source.value),
|
|
6909
|
+
sourceCsvColumns: readSourceCsvColumnSpecs(source.value)
|
|
6910
|
+
};
|
|
6911
|
+
case "play":
|
|
6912
|
+
case "providers":
|
|
6913
|
+
return {
|
|
6914
|
+
packagedSourceCsvPath: null,
|
|
6915
|
+
sourceCsvColumns: []
|
|
6916
|
+
};
|
|
6917
|
+
}
|
|
6918
|
+
}
|
|
6919
|
+
function errorMessage(error) {
|
|
6920
|
+
return error instanceof Error ? error.message : String(error);
|
|
6921
|
+
}
|
|
6922
|
+
function renderPlayBootstrapError(error) {
|
|
6923
|
+
console.error(errorMessage(error));
|
|
6924
|
+
return error instanceof PlayBootstrapError ? error.exitCode : 1;
|
|
6925
|
+
}
|
|
6926
|
+
async function runPlayBootstrap(args) {
|
|
6927
|
+
const options = parsePlayBootstrapOptions(args);
|
|
6928
|
+
const client = new DeeplineClient();
|
|
6929
|
+
const contracts = await loadBootstrapContracts(client, options);
|
|
6930
|
+
const csvContext = loadCsvContext(options.from);
|
|
6931
|
+
const source = generateBootstrapPlaySource({
|
|
6932
|
+
options,
|
|
6933
|
+
...contracts,
|
|
6934
|
+
...csvContext
|
|
6935
|
+
});
|
|
6936
|
+
process.stdout.write(source);
|
|
6937
|
+
return 0;
|
|
6938
|
+
}
|
|
6939
|
+
async function handlePlayBootstrap(args) {
|
|
6940
|
+
return runPlayBootstrap(args).catch(renderPlayBootstrapError);
|
|
6941
|
+
}
|
|
6942
|
+
function registerPlayBootstrapCommand(play) {
|
|
6943
|
+
play.command("bootstrap <template>").description("Print a scratchpad play for a GTM route template.").addHelpText(
|
|
6944
|
+
"after",
|
|
6945
|
+
`
|
|
6946
|
+
Notes:
|
|
6947
|
+
Cloud-validated play generator for agents. Pick the JTBD as the positional
|
|
6948
|
+
template, bind resources with typed refs, redirect stdout to a .play.ts file,
|
|
6949
|
+
then edit the generated TODO mapping comments. Multiple finder providers are
|
|
6950
|
+
generated as a waterfall in the order you pass them.
|
|
6951
|
+
|
|
6952
|
+
This command requires auth because provider/play contracts are fetched from
|
|
6953
|
+
Deepline. It prints TypeScript source only and does not run paid tools; the
|
|
6954
|
+
generated play may spend credits later when you run it.
|
|
6955
|
+
|
|
6956
|
+
stdout is the generated .play.ts source. Errors and diagnostics go to stderr.
|
|
6957
|
+
There is no JSON mode and no --out; use shell redirection:
|
|
6958
|
+
deepline plays bootstrap ... > scratchpad.play.ts
|
|
6959
|
+
|
|
6960
|
+
Templates:
|
|
6961
|
+
people-list start from people/contact rows
|
|
6962
|
+
company-list start from company/account rows
|
|
6963
|
+
people-email people/contact rows -> email finder
|
|
6964
|
+
people-phone people/contact rows -> phone finder
|
|
6965
|
+
company-people company/account rows -> people play
|
|
6966
|
+
company-people-email company/account rows -> people play -> email finder
|
|
6967
|
+
company-people-phone company/account rows -> people play -> phone finder
|
|
6968
|
+
|
|
6969
|
+
Resource refs:
|
|
6970
|
+
csv:./contacts.csv
|
|
6971
|
+
play:prebuilt/name-and-domain-to-email-waterfall
|
|
6972
|
+
provider:dropleads_search_people
|
|
6973
|
+
providers:hunter_email_finder,leadmagic_email_finder
|
|
6974
|
+
|
|
6975
|
+
Validation:
|
|
6976
|
+
source providers must match the route entity: company_search or people_search
|
|
6977
|
+
source providers must expose list getters
|
|
6978
|
+
company -> people uses a play only; provider bridge is generated later when a play exists
|
|
6979
|
+
email/phone finder providers must match their category and expose value getters
|
|
6980
|
+
finder plays/providers must match the route; generated code leaves input mapping explicit
|
|
6981
|
+
business-specific provider inputs and company -> people persona fields are TODOs in code
|
|
6982
|
+
csv: paths are resolved from the directory where you run bootstrap; redirect the play file there too
|
|
6983
|
+
|
|
6984
|
+
Exit codes:
|
|
6985
|
+
0 success
|
|
6986
|
+
2 usage/local input error
|
|
6987
|
+
7 route validation failed
|
|
6988
|
+
|
|
6989
|
+
Examples:
|
|
6990
|
+
deepline plays bootstrap people-email --from csv:data/leads.csv --using play:prebuilt/name-and-domain-to-email-waterfall --limit 5 > email-flow.play.ts
|
|
6991
|
+
deepline plays check email-flow.play.ts
|
|
6992
|
+
deepline plays run email-flow.play.ts --input '{"limit":5}' --watch
|
|
6993
|
+
|
|
6994
|
+
deepline plays bootstrap people-email --from provider:dropleads_search_people --using providers:hunter_email_finder,leadmagic_email_finder --limit 5 > prospecting.play.ts
|
|
6995
|
+
deepline plays bootstrap company-people-email --from provider:apollo_company_search --people play:prebuilt/company-to-contact --email play:prebuilt/name-and-domain-to-email-waterfall --limit 5 > account-contacts.play.ts
|
|
6996
|
+
deepline plays bootstrap company-list --from provider:apollo_company_search --limit 5 > companies.play.ts
|
|
6997
|
+
`
|
|
6998
|
+
).option("--name <name>", "Generated play name").option(
|
|
6999
|
+
"--from <ref>",
|
|
7000
|
+
"Route source: csv:PATH, play:REF, provider:ID, or providers:ID,ID"
|
|
7001
|
+
).option(
|
|
7002
|
+
"--using <ref>",
|
|
7003
|
+
"Single-stage route shorthand for people-email, people-phone, or company-people"
|
|
7004
|
+
).option(
|
|
7005
|
+
"--people <ref>",
|
|
7006
|
+
"Company-to-people stage for company-people-email/phone; use play:REF"
|
|
7007
|
+
).option(
|
|
7008
|
+
"--email <ref>",
|
|
7009
|
+
"Email finder stage: play:REF, provider:ID, or providers:ID,ID"
|
|
7010
|
+
).option(
|
|
7011
|
+
"--phone <ref>",
|
|
7012
|
+
"Phone finder stage: play:REF, provider:ID, or providers:ID,ID"
|
|
7013
|
+
).option("--limit <n>", "Maximum rows to fan out in the generated play").action(async (template, options) => {
|
|
7014
|
+
process.exitCode = await handlePlayBootstrap([
|
|
7015
|
+
template,
|
|
7016
|
+
...options.name ? ["--name", options.name] : [],
|
|
7017
|
+
...options.from ? ["--from", options.from] : [],
|
|
7018
|
+
...options.using ? ["--using", options.using] : [],
|
|
7019
|
+
...options.people ? ["--people", options.people] : [],
|
|
7020
|
+
...options.email ? ["--email", options.email] : [],
|
|
7021
|
+
...options.phone ? ["--phone", options.phone] : [],
|
|
7022
|
+
...options.limit ? ["--limit", options.limit] : []
|
|
7023
|
+
]);
|
|
5584
7024
|
});
|
|
5585
7025
|
}
|
|
5586
7026
|
|
|
@@ -6035,9 +7475,9 @@ function traceCliSync(phase, fields, run) {
|
|
|
6035
7475
|
}
|
|
6036
7476
|
}
|
|
6037
7477
|
function sleep4(ms) {
|
|
6038
|
-
return new Promise((
|
|
7478
|
+
return new Promise((resolve12) => setTimeout(resolve12, ms));
|
|
6039
7479
|
}
|
|
6040
|
-
function
|
|
7480
|
+
function parseReferencedPlayTarget2(target) {
|
|
6041
7481
|
const trimmed = target.trim();
|
|
6042
7482
|
const slashIndex = trimmed.indexOf("/");
|
|
6043
7483
|
if (slashIndex <= 0 || slashIndex === trimmed.length - 1) {
|
|
@@ -6058,7 +7498,7 @@ function buildBarePrebuiltReferenceError(input) {
|
|
|
6058
7498
|
);
|
|
6059
7499
|
}
|
|
6060
7500
|
async function assertCanonicalNamedPlayReference(client, target) {
|
|
6061
|
-
const parsed =
|
|
7501
|
+
const parsed = parseReferencedPlayTarget2(target);
|
|
6062
7502
|
const detail = await client.getPlay(parsed.playName);
|
|
6063
7503
|
if (detail.play.ownerType === "deepline" && !isPrebuiltReferenceTarget(target)) {
|
|
6064
7504
|
throw buildBarePrebuiltReferenceError({
|
|
@@ -6079,10 +7519,10 @@ function formatPlayListReference(play) {
|
|
|
6079
7519
|
return play.reference || play.name;
|
|
6080
7520
|
}
|
|
6081
7521
|
function defaultMaterializedPlayPath(reference) {
|
|
6082
|
-
return
|
|
7522
|
+
return resolve10(defaultStarterPlayPath(reference));
|
|
6083
7523
|
}
|
|
6084
7524
|
function defaultStarterPlayPath(reference) {
|
|
6085
|
-
const playName =
|
|
7525
|
+
const playName = parseReferencedPlayTarget2(reference).unqualifiedPlayName;
|
|
6086
7526
|
const safeName = playName.trim().toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
6087
7527
|
return `./${safeName || "play"}.play.ts`;
|
|
6088
7528
|
}
|
|
@@ -6138,7 +7578,7 @@ To make your own version:
|
|
|
6138
7578
|
);
|
|
6139
7579
|
}
|
|
6140
7580
|
async function ensureEditableRemotePlay(client, target) {
|
|
6141
|
-
const parsed =
|
|
7581
|
+
const parsed = parseReferencedPlayTarget2(target);
|
|
6142
7582
|
const detail = await client.getPlay(parsed.playName);
|
|
6143
7583
|
if (detail.play.ownerType === "deepline") {
|
|
6144
7584
|
throw buildReadonlyPrebuiltPlayError(formatPlayReference(detail.play));
|
|
@@ -6158,7 +7598,7 @@ function extractPlayName(code, filePath) {
|
|
|
6158
7598
|
throw buildMissingDefinePlayError(filePath);
|
|
6159
7599
|
}
|
|
6160
7600
|
function isFileTarget(target) {
|
|
6161
|
-
return existsSync6(
|
|
7601
|
+
return existsSync6(resolve10(target));
|
|
6162
7602
|
}
|
|
6163
7603
|
function looksLikeRunId(target) {
|
|
6164
7604
|
return /^play\/[^/]+\/run\/[^/]+/.test(target.trim());
|
|
@@ -6179,7 +7619,7 @@ function looksLikeFilePath(target) {
|
|
|
6179
7619
|
}
|
|
6180
7620
|
return target.includes("\\") || /\.(ts|js|mjs|play\.ts)$/.test(target);
|
|
6181
7621
|
}
|
|
6182
|
-
function
|
|
7622
|
+
function parsePositiveInteger3(value, flagName) {
|
|
6183
7623
|
const parsed = Number.parseInt(value, 10);
|
|
6184
7624
|
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
6185
7625
|
throw new Error(`${flagName} must be a positive integer.`);
|
|
@@ -6187,7 +7627,7 @@ function parsePositiveInteger2(value, flagName) {
|
|
|
6187
7627
|
return parsed;
|
|
6188
7628
|
}
|
|
6189
7629
|
function parseJsonInput(raw) {
|
|
6190
|
-
const source = raw.startsWith("@") ? readFileSync5(
|
|
7630
|
+
const source = raw.startsWith("@") ? readFileSync5(resolve10(raw.slice(1)), "utf-8") : raw;
|
|
6191
7631
|
const parsed = JSON.parse(source);
|
|
6192
7632
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
6193
7633
|
throw new Error("--input must be a JSON object.");
|
|
@@ -6254,7 +7694,8 @@ function setDottedInputValue(input, path, value) {
|
|
|
6254
7694
|
cursor[parts[parts.length - 1]] = value;
|
|
6255
7695
|
}
|
|
6256
7696
|
function schemaMetadata(schema, key) {
|
|
6257
|
-
if (!schema || typeof schema !== "object" || Array.isArray(schema))
|
|
7697
|
+
if (!schema || typeof schema !== "object" || Array.isArray(schema))
|
|
7698
|
+
return null;
|
|
6258
7699
|
const value = schema[key];
|
|
6259
7700
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
6260
7701
|
}
|
|
@@ -6288,7 +7729,7 @@ function fileInputBindingsFromStaticPipeline(staticPipeline) {
|
|
|
6288
7729
|
function isLocalFilePathValue(value) {
|
|
6289
7730
|
if (typeof value !== "string" || !value.trim()) return false;
|
|
6290
7731
|
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(value.trim())) return false;
|
|
6291
|
-
return existsSync6(
|
|
7732
|
+
return existsSync6(resolve10(value));
|
|
6292
7733
|
}
|
|
6293
7734
|
function inputContainsLocalFilePath(value) {
|
|
6294
7735
|
if (isLocalFilePathValue(value)) {
|
|
@@ -6309,12 +7750,14 @@ function namedRunNeedsPlayDefinition(input) {
|
|
|
6309
7750
|
}
|
|
6310
7751
|
async function stageFileInputArgs(input) {
|
|
6311
7752
|
const uniqueBindings = [
|
|
6312
|
-
...new Map(
|
|
7753
|
+
...new Map(
|
|
7754
|
+
input.bindings.map((binding) => [binding.inputPath, binding])
|
|
7755
|
+
).values()
|
|
6313
7756
|
];
|
|
6314
7757
|
const localFiles = uniqueBindings.flatMap((binding) => {
|
|
6315
7758
|
const value = getDottedInputValue(input.runtimeInput, binding.inputPath);
|
|
6316
7759
|
if (!isLocalFilePathValue(value)) return [];
|
|
6317
|
-
const absolutePath =
|
|
7760
|
+
const absolutePath = resolve10(value);
|
|
6318
7761
|
return [{ binding, absolutePath, logicalPath: basename3(absolutePath) }];
|
|
6319
7762
|
});
|
|
6320
7763
|
if (localFiles.length === 0) {
|
|
@@ -6327,10 +7770,18 @@ async function stageFileInputArgs(input) {
|
|
|
6327
7770
|
localFiles.map((file) => stageFile(file.logicalPath, file.absolutePath))
|
|
6328
7771
|
);
|
|
6329
7772
|
for (const [index, file] of localFiles.entries()) {
|
|
6330
|
-
setDottedInputValue(
|
|
7773
|
+
setDottedInputValue(
|
|
7774
|
+
input.runtimeInput,
|
|
7775
|
+
file.binding.inputPath,
|
|
7776
|
+
file.logicalPath
|
|
7777
|
+
);
|
|
6331
7778
|
const stagedFile = staged[index];
|
|
6332
7779
|
if (stagedFile && stagedFile.logicalPath !== file.logicalPath) {
|
|
6333
|
-
setDottedInputValue(
|
|
7780
|
+
setDottedInputValue(
|
|
7781
|
+
input.runtimeInput,
|
|
7782
|
+
file.binding.inputPath,
|
|
7783
|
+
stagedFile.logicalPath
|
|
7784
|
+
);
|
|
6334
7785
|
}
|
|
6335
7786
|
}
|
|
6336
7787
|
return {
|
|
@@ -6350,9 +7801,9 @@ function stageFile(logicalPath, absolutePath) {
|
|
|
6350
7801
|
}
|
|
6351
7802
|
function normalizePlayPath(filePath) {
|
|
6352
7803
|
try {
|
|
6353
|
-
return realpathSync.native(
|
|
7804
|
+
return realpathSync.native(resolve10(filePath));
|
|
6354
7805
|
} catch {
|
|
6355
|
-
return
|
|
7806
|
+
return resolve10(filePath);
|
|
6356
7807
|
}
|
|
6357
7808
|
}
|
|
6358
7809
|
function formatBundlingErrors(filePath, errors) {
|
|
@@ -6710,7 +8161,10 @@ function formatProgressCounts(input) {
|
|
|
6710
8161
|
if (completed === null || total === null || total <= 0) {
|
|
6711
8162
|
return null;
|
|
6712
8163
|
}
|
|
6713
|
-
const percent = Math.max(
|
|
8164
|
+
const percent = Math.max(
|
|
8165
|
+
0,
|
|
8166
|
+
Math.min(100, Math.round(completed / total * 100))
|
|
8167
|
+
);
|
|
6714
8168
|
const failed = typeof input.failed === "number" && Number.isFinite(input.failed) && input.failed > 0 ? `, failed ${formatInteger(input.failed)}` : "";
|
|
6715
8169
|
return `${formatInteger(completed)}/${formatInteger(total)} (${percent}%)${failed}`;
|
|
6716
8170
|
}
|
|
@@ -7424,7 +8878,9 @@ function buildInsufficientCreditsSummaryLines(input) {
|
|
|
7424
8878
|
const runId = input.status.runId?.trim();
|
|
7425
8879
|
if (runId) {
|
|
7426
8880
|
lines.push(` inspect: deepline runs get ${runId} --json`);
|
|
7427
|
-
lines.push(
|
|
8881
|
+
lines.push(
|
|
8882
|
+
` export partial: deepline runs export ${runId} --out output.csv`
|
|
8883
|
+
);
|
|
7428
8884
|
}
|
|
7429
8885
|
return lines;
|
|
7430
8886
|
}
|
|
@@ -7496,7 +8952,10 @@ function normalizeErrorsForEnvelope(status, error) {
|
|
|
7496
8952
|
}
|
|
7497
8953
|
return {
|
|
7498
8954
|
...entry,
|
|
7499
|
-
message: formatInsufficientCreditsMessage({
|
|
8955
|
+
message: formatInsufficientCreditsMessage({
|
|
8956
|
+
billing: billing2,
|
|
8957
|
+
error: message2
|
|
8958
|
+
}),
|
|
7500
8959
|
billing: stripProviderSpendFromBilling(billing2)
|
|
7501
8960
|
};
|
|
7502
8961
|
});
|
|
@@ -7651,7 +9110,9 @@ function buildRunPackageTextLines(packaged) {
|
|
|
7651
9110
|
const runId = typeof run.id === "string" ? run.id : "unknown";
|
|
7652
9111
|
const status = typeof run.status === "string" ? run.status : "unknown";
|
|
7653
9112
|
const playName = typeof run.playName === "string" ? run.playName : null;
|
|
7654
|
-
const lines = [
|
|
9113
|
+
const lines = [
|
|
9114
|
+
`${status === "completed" ? "\u2713" : status === "failed" ? "\u2717" : "\u2022"} ${status} ${runId}`
|
|
9115
|
+
];
|
|
7655
9116
|
if (playName) {
|
|
7656
9117
|
lines.push(` play: ${playName}`);
|
|
7657
9118
|
}
|
|
@@ -7722,13 +9183,16 @@ function writePlayResult(status, jsonOutput, options) {
|
|
|
7722
9183
|
}
|
|
7723
9184
|
lines.push(...renderedServerView.actions);
|
|
7724
9185
|
const payload = options?.fullJson ? enrichPlayStatusWithDatasetStats(status) : compactPlayStatus(status);
|
|
7725
|
-
printCommandEnvelope(
|
|
7726
|
-
|
|
7727
|
-
|
|
7728
|
-
|
|
7729
|
-
|
|
7730
|
-
|
|
7731
|
-
|
|
9186
|
+
printCommandEnvelope(
|
|
9187
|
+
{
|
|
9188
|
+
...payload,
|
|
9189
|
+
render: {
|
|
9190
|
+
sections: [{ title: "run result", lines }]
|
|
9191
|
+
}
|
|
9192
|
+
},
|
|
9193
|
+
{ json: jsonOutput, text: `${lines.join("\n")}
|
|
9194
|
+
` }
|
|
9195
|
+
);
|
|
7732
9196
|
}
|
|
7733
9197
|
async function resolvePlayRunOutputStatus(input) {
|
|
7734
9198
|
if (!input.fullJson || !getPlayRunPackage(input.status)) {
|
|
@@ -7753,7 +9217,7 @@ function sqlStringLiteral(value) {
|
|
|
7753
9217
|
return `'${value.replace(/'/g, "''")}'`;
|
|
7754
9218
|
}
|
|
7755
9219
|
function runExportRetryCommand(runId, outPath, datasetPath) {
|
|
7756
|
-
return `deepline runs export ${runId}${datasetPath ? ` --dataset ${shellSingleQuote(datasetPath)}` : ""} --out ${shellSingleQuote(
|
|
9220
|
+
return `deepline runs export ${runId}${datasetPath ? ` --dataset ${shellSingleQuote(datasetPath)}` : ""} --out ${shellSingleQuote(resolve10(outPath))}`;
|
|
7757
9221
|
}
|
|
7758
9222
|
function extractRunPlayName(status) {
|
|
7759
9223
|
const run = status.run;
|
|
@@ -7787,7 +9251,7 @@ function buildCustomerDbQueryPlan(input) {
|
|
|
7787
9251
|
return {
|
|
7788
9252
|
sql,
|
|
7789
9253
|
json: `${base} --json`,
|
|
7790
|
-
csv: `${base} --format csv --out ${shellSingleQuote(
|
|
9254
|
+
csv: `${base} --format csv --out ${shellSingleQuote(resolve10(input.outPath))}`
|
|
7791
9255
|
};
|
|
7792
9256
|
}
|
|
7793
9257
|
function exportableSheetRow(row) {
|
|
@@ -8087,7 +9551,9 @@ function renderServerResultView(value) {
|
|
|
8087
9551
|
(field) => typeof field === "string"
|
|
8088
9552
|
) : [];
|
|
8089
9553
|
if (rawResultFields.length > 0) {
|
|
8090
|
-
lines.push(
|
|
9554
|
+
lines.push(
|
|
9555
|
+
` tool-result columns: ${rawResultFields.join(", ")}`
|
|
9556
|
+
);
|
|
8091
9557
|
}
|
|
8092
9558
|
}
|
|
8093
9559
|
if (typeof table.slowExportAsCsvCommand === "string") {
|
|
@@ -8140,10 +9606,13 @@ function writeStartedPlayRun(input) {
|
|
|
8140
9606
|
` stop run: deepline runs stop ${input.runId} --reason "stale lock" --json`
|
|
8141
9607
|
];
|
|
8142
9608
|
if (input.jsonOutput) {
|
|
8143
|
-
printCommandEnvelope(
|
|
8144
|
-
|
|
8145
|
-
|
|
8146
|
-
|
|
9609
|
+
printCommandEnvelope(
|
|
9610
|
+
{
|
|
9611
|
+
...payload,
|
|
9612
|
+
render: { sections: [{ title: "play run", lines }] }
|
|
9613
|
+
},
|
|
9614
|
+
{ json: true }
|
|
9615
|
+
);
|
|
8147
9616
|
return;
|
|
8148
9617
|
}
|
|
8149
9618
|
if (input.dashboardUrl) {
|
|
@@ -8154,11 +9623,14 @@ function writeStartedPlayRun(input) {
|
|
|
8154
9623
|
input.progress.writeLine(output, process.stdout);
|
|
8155
9624
|
return;
|
|
8156
9625
|
}
|
|
8157
|
-
printCommandEnvelope(
|
|
8158
|
-
|
|
8159
|
-
|
|
8160
|
-
|
|
8161
|
-
|
|
9626
|
+
printCommandEnvelope(
|
|
9627
|
+
{
|
|
9628
|
+
...payload,
|
|
9629
|
+
render: { sections: [{ title: "play run", lines }] }
|
|
9630
|
+
},
|
|
9631
|
+
{ json: false, text: `${output}
|
|
9632
|
+
` }
|
|
9633
|
+
);
|
|
8162
9634
|
}
|
|
8163
9635
|
function parsePlayRunOptions(args) {
|
|
8164
9636
|
const usage = "Usage: deepline plays run <play-name> [--input '{...}'] [--no-wait] [--tail-timeout-ms 30000] [--force] [--full] [--<input> value]\n deepline plays run <play-file.ts> [--input '{...}'] [--no-wait] [--tail-timeout-ms 30000] [--force] [--full] [--<input> value]\n deepline plays run --file <play-file.ts> [--input '{...}'] [--no-wait] [--tail-timeout-ms 30000] [--force] [--full] [--<input> value]\n deepline plays run --name <name> [--input '{...}'] [--live|--latest|--revision-id <id>] [--no-wait] [--tail-timeout-ms 30000] [--force] [--no-open] [--json] [--full] [--<input> value]\n Unknown --<input> value flags, such as --limit 5, are passed into play input.\nRun `deepline plays run --help` for idempotency, tool call id, and ctx.map guidance.";
|
|
@@ -8181,7 +9653,7 @@ function parsePlayRunOptions(args) {
|
|
|
8181
9653
|
continue;
|
|
8182
9654
|
}
|
|
8183
9655
|
if (arg === "--name" && args[index + 1]) {
|
|
8184
|
-
playName =
|
|
9656
|
+
playName = parseReferencedPlayTarget2(args[++index]).playName;
|
|
8185
9657
|
continue;
|
|
8186
9658
|
}
|
|
8187
9659
|
if ((arg === "--input" || arg === "-i") && args[index + 1]) {
|
|
@@ -8211,7 +9683,7 @@ function parsePlayRunOptions(args) {
|
|
|
8211
9683
|
);
|
|
8212
9684
|
}
|
|
8213
9685
|
if ((arg === "--tail-timeout-ms" || arg === "--timeout-ms") && args[index + 1]) {
|
|
8214
|
-
waitTimeoutMs =
|
|
9686
|
+
waitTimeoutMs = parsePositiveInteger3(args[++index], arg);
|
|
8215
9687
|
continue;
|
|
8216
9688
|
}
|
|
8217
9689
|
if (PLAY_RUN_RESERVED_BOOLEAN_FLAGS.has(arg)) {
|
|
@@ -8245,7 +9717,7 @@ function parsePlayRunOptions(args) {
|
|
|
8245
9717
|
if (isFileTarget(arg) || looksLikeFilePath(arg)) {
|
|
8246
9718
|
filePath = arg;
|
|
8247
9719
|
} else {
|
|
8248
|
-
playName =
|
|
9720
|
+
playName = parseReferencedPlayTarget2(arg).playName;
|
|
8249
9721
|
}
|
|
8250
9722
|
continue;
|
|
8251
9723
|
}
|
|
@@ -8294,34 +9766,34 @@ function shouldUseLocalOnlyPlayCheck() {
|
|
|
8294
9766
|
const value = process.env.DEEPLINE_PLAY_CHECK_LOCAL_ONLY?.trim().toLowerCase();
|
|
8295
9767
|
return value === "1" || value === "true" || value === "yes" || value === "on";
|
|
8296
9768
|
}
|
|
8297
|
-
function
|
|
9769
|
+
function isRecord4(value) {
|
|
8298
9770
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
8299
9771
|
}
|
|
8300
|
-
function
|
|
9772
|
+
function stringValue2(value) {
|
|
8301
9773
|
return typeof value === "string" ? value.trim() : "";
|
|
8302
9774
|
}
|
|
8303
9775
|
function asArray(value) {
|
|
8304
9776
|
return Array.isArray(value) ? value : [];
|
|
8305
9777
|
}
|
|
8306
|
-
function
|
|
8307
|
-
if (Array.isArray(value)) return value.filter(
|
|
8308
|
-
if (!
|
|
9778
|
+
function extractionEntries2(value) {
|
|
9779
|
+
if (Array.isArray(value)) return value.filter(isRecord4);
|
|
9780
|
+
if (!isRecord4(value)) return [];
|
|
8309
9781
|
return Object.entries(value).map(
|
|
8310
|
-
([name, entry]) =>
|
|
9782
|
+
([name, entry]) => isRecord4(entry) ? { name, ...entry } : { name }
|
|
8311
9783
|
);
|
|
8312
9784
|
}
|
|
8313
9785
|
function firstRawPath(entry) {
|
|
8314
|
-
const details =
|
|
9786
|
+
const details = isRecord4(entry.details) ? entry.details : {};
|
|
8315
9787
|
const paths = [
|
|
8316
9788
|
...asArray(details.rawToolOutputPaths),
|
|
8317
9789
|
...asArray(details.raw_tool_output_paths),
|
|
8318
9790
|
...asArray(details.candidatePaths),
|
|
8319
9791
|
...asArray(details.candidate_paths)
|
|
8320
|
-
].map(
|
|
9792
|
+
].map(stringValue2).filter(Boolean);
|
|
8321
9793
|
return paths[0];
|
|
8322
9794
|
}
|
|
8323
9795
|
function checkHintExpression(value) {
|
|
8324
|
-
return
|
|
9796
|
+
return stringValue2(value).replace(/^toolExecutionResult\./, "result.");
|
|
8325
9797
|
}
|
|
8326
9798
|
function checkHintRawPath(value) {
|
|
8327
9799
|
return value?.replace(/^toolResponse\./, "result.toolResponse.");
|
|
@@ -8329,14 +9801,14 @@ function checkHintRawPath(value) {
|
|
|
8329
9801
|
function collectStaticPipelineToolIds(staticPipeline) {
|
|
8330
9802
|
const seen = /* @__PURE__ */ new Set();
|
|
8331
9803
|
const visitPipeline = (pipeline) => {
|
|
8332
|
-
if (!
|
|
9804
|
+
if (!isRecord4(pipeline)) return;
|
|
8333
9805
|
for (const step of [
|
|
8334
9806
|
...asArray(pipeline.stages),
|
|
8335
9807
|
...asArray(pipeline.substeps)
|
|
8336
9808
|
]) {
|
|
8337
|
-
if (!
|
|
9809
|
+
if (!isRecord4(step)) continue;
|
|
8338
9810
|
if (step.type === "tool") {
|
|
8339
|
-
const toolId =
|
|
9811
|
+
const toolId = stringValue2(step.toolId) || stringValue2(step.tool);
|
|
8340
9812
|
if (toolId) seen.add(toolId);
|
|
8341
9813
|
}
|
|
8342
9814
|
if (step.type === "play_call") {
|
|
@@ -8348,20 +9820,20 @@ function collectStaticPipelineToolIds(staticPipeline) {
|
|
|
8348
9820
|
return [...seen].sort();
|
|
8349
9821
|
}
|
|
8350
9822
|
function toolGetterHintFromMetadata(toolId, tool) {
|
|
8351
|
-
const usageGuidance =
|
|
8352
|
-
const resultGuidance =
|
|
8353
|
-
const toolResponse =
|
|
8354
|
-
const lists =
|
|
9823
|
+
const usageGuidance = isRecord4(tool.usageGuidance) ? tool.usageGuidance : {};
|
|
9824
|
+
const resultGuidance = isRecord4(usageGuidance.toolExecutionResult) ? usageGuidance.toolExecutionResult : isRecord4(usageGuidance.tool_execution_result) ? usageGuidance.tool_execution_result : {};
|
|
9825
|
+
const toolResponse = isRecord4(resultGuidance.toolResponse) ? resultGuidance.toolResponse : isRecord4(resultGuidance.tool_response) ? resultGuidance.tool_response : {};
|
|
9826
|
+
const lists = extractionEntries2(
|
|
8355
9827
|
resultGuidance.extractedLists ?? resultGuidance.extracted_lists
|
|
8356
9828
|
).map((entry) => ({
|
|
8357
|
-
name:
|
|
9829
|
+
name: stringValue2(entry.name),
|
|
8358
9830
|
expression: checkHintExpression(entry.expression),
|
|
8359
9831
|
raw: checkHintRawPath(firstRawPath(entry))
|
|
8360
9832
|
})).filter((entry) => entry.name && entry.expression);
|
|
8361
|
-
const values =
|
|
9833
|
+
const values = extractionEntries2(
|
|
8362
9834
|
resultGuidance.extractedValues ?? resultGuidance.extracted_values
|
|
8363
9835
|
).map((entry) => ({
|
|
8364
|
-
name:
|
|
9836
|
+
name: stringValue2(entry.name),
|
|
8365
9837
|
expression: checkHintExpression(entry.expression),
|
|
8366
9838
|
raw: checkHintRawPath(firstRawPath(entry))
|
|
8367
9839
|
})).filter((entry) => entry.name && entry.expression);
|
|
@@ -8417,11 +9889,11 @@ function printToolGetterHints(hints) {
|
|
|
8417
9889
|
async function handlePlayCheck(args) {
|
|
8418
9890
|
const options = parsePlayCheckOptions(args);
|
|
8419
9891
|
if (!isFileTarget(options.target)) {
|
|
8420
|
-
const resolved =
|
|
9892
|
+
const resolved = resolve10(options.target);
|
|
8421
9893
|
console.error(`File not found: ${resolved}`);
|
|
8422
9894
|
return 1;
|
|
8423
9895
|
}
|
|
8424
|
-
const absolutePlayPath =
|
|
9896
|
+
const absolutePlayPath = resolve10(options.target);
|
|
8425
9897
|
const sourceCode = readFileSync5(absolutePlayPath, "utf-8");
|
|
8426
9898
|
let graph;
|
|
8427
9899
|
try {
|
|
@@ -8449,8 +9921,10 @@ async function handlePlayCheck(args) {
|
|
|
8449
9921
|
graphHash: graph.root.artifact.graphHash
|
|
8450
9922
|
};
|
|
8451
9923
|
if (options.jsonOutput) {
|
|
8452
|
-
process.stdout.write(
|
|
8453
|
-
|
|
9924
|
+
process.stdout.write(
|
|
9925
|
+
`${JSON.stringify({ name: playName, ...result2 })}
|
|
9926
|
+
`
|
|
9927
|
+
);
|
|
8454
9928
|
} else {
|
|
8455
9929
|
console.log(`\u2713 ${playName} passed local play check`);
|
|
8456
9930
|
console.log(` artifact: ${result2.artifactHash.slice(0, 12)}`);
|
|
@@ -8474,8 +9948,10 @@ async function handlePlayCheck(args) {
|
|
|
8474
9948
|
toolGetterHints: result.toolGetterHints ?? await buildToolGetterHints(client, result.staticPipeline)
|
|
8475
9949
|
};
|
|
8476
9950
|
if (options.jsonOutput) {
|
|
8477
|
-
process.stdout.write(
|
|
8478
|
-
|
|
9951
|
+
process.stdout.write(
|
|
9952
|
+
`${JSON.stringify({ name: playName, ...enrichedResult })}
|
|
9953
|
+
`
|
|
9954
|
+
);
|
|
8479
9955
|
} else if (enrichedResult.valid) {
|
|
8480
9956
|
console.log(`\u2713 ${playName} passed cloud play check`);
|
|
8481
9957
|
if (enrichedResult.artifactHash) {
|
|
@@ -8497,7 +9973,7 @@ async function handleFileBackedRun(options) {
|
|
|
8497
9973
|
}
|
|
8498
9974
|
const client = new DeeplineClient();
|
|
8499
9975
|
const progress = getActiveCliProgress() ?? createCliProgress(!options.jsonOutput);
|
|
8500
|
-
const absolutePlayPath =
|
|
9976
|
+
const absolutePlayPath = resolve10(options.target.path);
|
|
8501
9977
|
progress.phase("compiling play");
|
|
8502
9978
|
const sourceCode = traceCliSync(
|
|
8503
9979
|
"cli.play_file_read_source",
|
|
@@ -8786,7 +10262,7 @@ async function handlePlayRun(args) {
|
|
|
8786
10262
|
if (isFileTarget(options.target.path)) {
|
|
8787
10263
|
return handleFileBackedRun(options);
|
|
8788
10264
|
}
|
|
8789
|
-
const resolved =
|
|
10265
|
+
const resolved = resolve10(options.target.path);
|
|
8790
10266
|
console.error(`File not found: ${resolved}`);
|
|
8791
10267
|
const dir = dirname8(resolved);
|
|
8792
10268
|
if (existsSync6(dir)) {
|
|
@@ -8852,7 +10328,7 @@ async function handleRunsList(args) {
|
|
|
8852
10328
|
for (let index = 0; index < args.length; index += 1) {
|
|
8853
10329
|
const arg = args[index];
|
|
8854
10330
|
if ((arg === "--play" || arg === "--name") && args[index + 1]) {
|
|
8855
|
-
playName =
|
|
10331
|
+
playName = parseReferencedPlayTarget2(args[++index]).playName;
|
|
8856
10332
|
continue;
|
|
8857
10333
|
}
|
|
8858
10334
|
if (arg === "--status" && args[index + 1]) {
|
|
@@ -8881,15 +10357,20 @@ async function handleRunsList(args) {
|
|
|
8881
10357
|
executionTime: run.executionTime,
|
|
8882
10358
|
playName: run.memo?.playName ?? playName
|
|
8883
10359
|
}));
|
|
8884
|
-
const lines = runs.length === 0 ? [`No runs found for ${playName}.`] : runs.map(
|
|
8885
|
-
|
|
8886
|
-
|
|
8887
|
-
|
|
8888
|
-
|
|
8889
|
-
|
|
10360
|
+
const lines = runs.length === 0 ? [`No runs found for ${playName}.`] : runs.map(
|
|
10361
|
+
(run) => `${run.runId} ${run.status} ${formatTimestamp(run.startedAt)}`
|
|
10362
|
+
);
|
|
10363
|
+
printCommandEnvelope(
|
|
10364
|
+
{
|
|
10365
|
+
runs,
|
|
10366
|
+
count: runs.length,
|
|
10367
|
+
next: {
|
|
10368
|
+
get: runs[0]?.runId ? `deepline runs get ${runs[0].runId} --json` : null
|
|
10369
|
+
},
|
|
10370
|
+
render: { sections: [{ title: "runs", lines }] }
|
|
8890
10371
|
},
|
|
8891
|
-
|
|
8892
|
-
|
|
10372
|
+
{ json: argsWantJson(args) }
|
|
10373
|
+
);
|
|
8893
10374
|
return 0;
|
|
8894
10375
|
}
|
|
8895
10376
|
async function handleRunTail(args) {
|
|
@@ -8904,7 +10385,9 @@ async function handleRunTail(args) {
|
|
|
8904
10385
|
for (let index = 0; index < args.length; index += 1) {
|
|
8905
10386
|
const arg = args[index];
|
|
8906
10387
|
if (arg === "--cursor") {
|
|
8907
|
-
console.error(
|
|
10388
|
+
console.error(
|
|
10389
|
+
"--cursor was removed. deepline runs tail reads the canonical run stream."
|
|
10390
|
+
);
|
|
8908
10391
|
return 1;
|
|
8909
10392
|
}
|
|
8910
10393
|
if (arg.startsWith("--") && arg !== "--json" && arg !== "--compact") {
|
|
@@ -8931,11 +10414,11 @@ async function handleRunLogs(args) {
|
|
|
8931
10414
|
for (let index = 0; index < args.length; index += 1) {
|
|
8932
10415
|
const arg = args[index];
|
|
8933
10416
|
if (arg === "--limit" && args[index + 1]) {
|
|
8934
|
-
limit =
|
|
10417
|
+
limit = parsePositiveInteger3(args[++index], "--limit");
|
|
8935
10418
|
continue;
|
|
8936
10419
|
}
|
|
8937
10420
|
if (arg === "--out" && args[index + 1]) {
|
|
8938
|
-
outPath =
|
|
10421
|
+
outPath = resolve10(args[++index]);
|
|
8939
10422
|
}
|
|
8940
10423
|
}
|
|
8941
10424
|
const client = new DeeplineClient();
|
|
@@ -8943,30 +10426,46 @@ async function handleRunLogs(args) {
|
|
|
8943
10426
|
const logs = status.progress?.logs ?? [];
|
|
8944
10427
|
if (outPath) {
|
|
8945
10428
|
writeFileSync6(outPath, `${logs.join("\n")}${logs.length > 0 ? "\n" : ""}`);
|
|
8946
|
-
printCommandEnvelope(
|
|
8947
|
-
|
|
8948
|
-
|
|
8949
|
-
|
|
8950
|
-
|
|
8951
|
-
|
|
8952
|
-
|
|
10429
|
+
printCommandEnvelope(
|
|
10430
|
+
{
|
|
10431
|
+
runId: status.runId,
|
|
10432
|
+
log_path: outPath,
|
|
10433
|
+
lineCount: logs.length,
|
|
10434
|
+
local: { log_path: outPath },
|
|
10435
|
+
render: {
|
|
10436
|
+
sections: [
|
|
10437
|
+
{
|
|
10438
|
+
title: "run logs",
|
|
10439
|
+
lines: [`Wrote ${logs.length} log lines to ${outPath}`]
|
|
10440
|
+
}
|
|
10441
|
+
]
|
|
10442
|
+
}
|
|
10443
|
+
},
|
|
10444
|
+
{ json: argsWantJson(args) }
|
|
10445
|
+
);
|
|
8953
10446
|
return 0;
|
|
8954
10447
|
}
|
|
8955
10448
|
const entries = logs.slice(Math.max(0, logs.length - limit));
|
|
8956
|
-
printCommandEnvelope(
|
|
8957
|
-
|
|
8958
|
-
|
|
8959
|
-
|
|
8960
|
-
|
|
8961
|
-
|
|
8962
|
-
|
|
8963
|
-
|
|
8964
|
-
|
|
8965
|
-
|
|
8966
|
-
|
|
10449
|
+
printCommandEnvelope(
|
|
10450
|
+
{
|
|
10451
|
+
runId: status.runId,
|
|
10452
|
+
totalCount: logs.length,
|
|
10453
|
+
returnedCount: entries.length,
|
|
10454
|
+
firstSequence: logs.length === 0 ? null : logs.length - entries.length + 1,
|
|
10455
|
+
lastSequence: logs.length === 0 ? null : logs.length,
|
|
10456
|
+
truncated: logs.length > entries.length,
|
|
10457
|
+
hasMore: logs.length > entries.length,
|
|
10458
|
+
entries,
|
|
10459
|
+
next: {
|
|
10460
|
+
export: `deepline runs logs ${status.runId} --out run.log --json`
|
|
10461
|
+
},
|
|
10462
|
+
render: { sections: [{ title: "run logs", lines: entries }] }
|
|
8967
10463
|
},
|
|
8968
|
-
|
|
8969
|
-
|
|
10464
|
+
{
|
|
10465
|
+
json: argsWantJson(args),
|
|
10466
|
+
text: `${entries.join("\n")}${entries.length > 0 ? "\n" : ""}`
|
|
10467
|
+
}
|
|
10468
|
+
);
|
|
8970
10469
|
return 0;
|
|
8971
10470
|
}
|
|
8972
10471
|
async function handleRunStop(args) {
|
|
@@ -8991,10 +10490,13 @@ async function handleRunStop(args) {
|
|
|
8991
10490
|
`Stopped ${result.runId}`,
|
|
8992
10491
|
...result.hitlCancelledCount > 0 ? [`cancelled HITL waits: ${result.hitlCancelledCount}`] : []
|
|
8993
10492
|
];
|
|
8994
|
-
printCommandEnvelope(
|
|
8995
|
-
|
|
8996
|
-
|
|
8997
|
-
|
|
10493
|
+
printCommandEnvelope(
|
|
10494
|
+
{
|
|
10495
|
+
...result,
|
|
10496
|
+
render: { sections: [{ title: "run stop", lines }] }
|
|
10497
|
+
},
|
|
10498
|
+
{ json: argsWantJson(args) }
|
|
10499
|
+
);
|
|
8998
10500
|
return 0;
|
|
8999
10501
|
}
|
|
9000
10502
|
async function handleRunExport(args) {
|
|
@@ -9012,7 +10514,7 @@ async function handleRunExport(args) {
|
|
|
9012
10514
|
for (let index = 0; index < args.length; index += 1) {
|
|
9013
10515
|
const arg = args[index];
|
|
9014
10516
|
if (arg === "--out" && args[index + 1]) {
|
|
9015
|
-
outPath =
|
|
10517
|
+
outPath = resolve10(args[++index]);
|
|
9016
10518
|
continue;
|
|
9017
10519
|
}
|
|
9018
10520
|
if (arg === "--dataset" && args[index + 1]) {
|
|
@@ -9020,7 +10522,7 @@ async function handleRunExport(args) {
|
|
|
9020
10522
|
continue;
|
|
9021
10523
|
}
|
|
9022
10524
|
if (arg === "--metadata-out" && args[index + 1]) {
|
|
9023
|
-
metadataOutPath =
|
|
10525
|
+
metadataOutPath = resolve10(args[++index]);
|
|
9024
10526
|
}
|
|
9025
10527
|
}
|
|
9026
10528
|
if (!outPath) {
|
|
@@ -9107,10 +10609,10 @@ async function handlePlayGet(args) {
|
|
|
9107
10609
|
for (let index = 1; index < args.length; index += 1) {
|
|
9108
10610
|
const arg = args[index];
|
|
9109
10611
|
if (arg === "--out" && args[index + 1]) {
|
|
9110
|
-
outPath =
|
|
10612
|
+
outPath = resolve10(args[++index]);
|
|
9111
10613
|
}
|
|
9112
10614
|
}
|
|
9113
|
-
const playName = isFileTarget(target) ? extractPlayName(readFileSync5(
|
|
10615
|
+
const playName = isFileTarget(target) ? extractPlayName(readFileSync5(resolve10(target), "utf-8"), resolve10(target)) : parseReferencedPlayTarget2(target).playName;
|
|
9114
10616
|
const detail = isFileTarget(target) ? await client.getPlay(playName) : await assertCanonicalNamedPlayReference(client, target);
|
|
9115
10617
|
const resolvedSource = detail.play.workingRevision?.sourceCode ?? detail.play.liveRevision?.sourceCode ?? detail.play.currentRevision?.sourceCode ?? detail.play.sourceCode ?? "";
|
|
9116
10618
|
const materializedFile = outPath ? materializeRemotePlaySource({
|
|
@@ -9195,7 +10697,7 @@ async function handlePlayVersions(args) {
|
|
|
9195
10697
|
const jsonOutput = argsWantJson(args);
|
|
9196
10698
|
await assertCanonicalNamedPlayReference(client, playName);
|
|
9197
10699
|
const versions = await client.listPlayVersions(
|
|
9198
|
-
|
|
10700
|
+
parseReferencedPlayTarget2(playName).playName
|
|
9199
10701
|
);
|
|
9200
10702
|
if (jsonOutput) {
|
|
9201
10703
|
process.stdout.write(`${JSON.stringify({ versions })}
|
|
@@ -9329,7 +10831,8 @@ function compactPlaySchema(schema) {
|
|
|
9329
10831
|
return fields.length > 0 ? { fields } : schema;
|
|
9330
10832
|
}
|
|
9331
10833
|
function playSchemaMetadata(schema, key) {
|
|
9332
|
-
if (!schema || typeof schema !== "object" || Array.isArray(schema))
|
|
10834
|
+
if (!schema || typeof schema !== "object" || Array.isArray(schema))
|
|
10835
|
+
return null;
|
|
9333
10836
|
const value = schema[key];
|
|
9334
10837
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
9335
10838
|
}
|
|
@@ -9344,7 +10847,10 @@ function playRunCommand(play, options) {
|
|
|
9344
10847
|
function summarizePlayListItemForCli(play, options) {
|
|
9345
10848
|
const aliases = play.aliases?.length ? play.aliases : [play.name];
|
|
9346
10849
|
const csvInput = playSchemaMetadata(play.inputSchema, "csvInput");
|
|
9347
|
-
const rowOutputSchema = playSchemaMetadata(
|
|
10850
|
+
const rowOutputSchema = playSchemaMetadata(
|
|
10851
|
+
play.outputSchema,
|
|
10852
|
+
"rowOutputSchema"
|
|
10853
|
+
);
|
|
9348
10854
|
const runCommand2 = playRunCommand(play, { csvInput });
|
|
9349
10855
|
const cloneEditStarter = buildCloneEditStarter(play);
|
|
9350
10856
|
return {
|
|
@@ -9494,7 +11000,7 @@ async function handlePlayDescribe(args) {
|
|
|
9494
11000
|
const client = new DeeplineClient();
|
|
9495
11001
|
await assertCanonicalNamedPlayReference(client, playName);
|
|
9496
11002
|
const play = await client.describePlay(
|
|
9497
|
-
|
|
11003
|
+
parseReferencedPlayTarget2(playName).playName,
|
|
9498
11004
|
{
|
|
9499
11005
|
compact: args.includes("--compact")
|
|
9500
11006
|
}
|
|
@@ -9540,7 +11046,7 @@ async function handlePlayPublish(args) {
|
|
|
9540
11046
|
}
|
|
9541
11047
|
let graph;
|
|
9542
11048
|
try {
|
|
9543
|
-
graph = await collectBundledPlayGraph(
|
|
11049
|
+
graph = await collectBundledPlayGraph(resolve10(playName));
|
|
9544
11050
|
await compileBundledPlayGraphManifests(client, graph);
|
|
9545
11051
|
await publishImportedPlayDependencies(client, graph);
|
|
9546
11052
|
} catch (error) {
|
|
@@ -9569,7 +11075,7 @@ async function handlePlayPublish(args) {
|
|
|
9569
11075
|
);
|
|
9570
11076
|
return 0;
|
|
9571
11077
|
}
|
|
9572
|
-
const resolvedName =
|
|
11078
|
+
const resolvedName = parseReferencedPlayTarget2(playName).playName;
|
|
9573
11079
|
if (useLatest) {
|
|
9574
11080
|
const versions = await client.listPlayVersions(resolvedName);
|
|
9575
11081
|
const latest = versions[0];
|
|
@@ -9609,17 +11115,19 @@ async function handlePlayDelete(args) {
|
|
|
9609
11115
|
const client = new DeeplineClient();
|
|
9610
11116
|
let detail;
|
|
9611
11117
|
try {
|
|
9612
|
-
detail = await client.getPlay(
|
|
11118
|
+
detail = await client.getPlay(parseReferencedPlayTarget2(playName).playName);
|
|
9613
11119
|
} catch (error) {
|
|
9614
11120
|
console.error(error instanceof Error ? error.message : String(error));
|
|
9615
11121
|
return 1;
|
|
9616
11122
|
}
|
|
9617
11123
|
if (detail.play.ownerType === "deepline" || detail.play.origin === "prebuilt") {
|
|
9618
|
-
console.error(
|
|
11124
|
+
console.error(
|
|
11125
|
+
`Cannot delete prebuilt play: ${formatPlayReference(detail.play)}`
|
|
11126
|
+
);
|
|
9619
11127
|
return 1;
|
|
9620
11128
|
}
|
|
9621
11129
|
const result = await client.deletePlay(
|
|
9622
|
-
|
|
11130
|
+
parseReferencedPlayTarget2(formatPlayReference(detail.play)).playName
|
|
9623
11131
|
);
|
|
9624
11132
|
if (argsWantJson(args)) {
|
|
9625
11133
|
process.stdout.write(`${JSON.stringify(result)}
|
|
@@ -9764,6 +11272,7 @@ Pass-through input flags:
|
|
|
9764
11272
|
...passthroughArgs
|
|
9765
11273
|
]);
|
|
9766
11274
|
});
|
|
11275
|
+
registerPlayBootstrapCommand(play);
|
|
9767
11276
|
play.command("get <target>").description("Fetch full play details.").addHelpText(
|
|
9768
11277
|
"after",
|
|
9769
11278
|
`
|
|
@@ -9778,7 +11287,10 @@ Examples:
|
|
|
9778
11287
|
deepline plays get prebuilt/name-and-domain-to-email-waterfall-batch --source > email-waterfall.play.ts
|
|
9779
11288
|
deepline plays get prebuilt/name-and-domain-to-email-waterfall-batch --source --out ./email-waterfall.play.ts
|
|
9780
11289
|
`
|
|
9781
|
-
).option("--json", "Emit JSON output. Also automatic when stdout is piped").option(
|
|
11290
|
+
).option("--json", "Emit JSON output. Also automatic when stdout is piped").option(
|
|
11291
|
+
"--source",
|
|
11292
|
+
"Print raw source code; combine with --out to write a file"
|
|
11293
|
+
).option("--out <path>", "Write source to a specific path").action(async (target, options) => {
|
|
9782
11294
|
process.exitCode = await handlePlayGet([
|
|
9783
11295
|
target,
|
|
9784
11296
|
...options.json ? ["--json"] : [],
|
|
@@ -9825,7 +11337,9 @@ Examples:
|
|
|
9825
11337
|
]);
|
|
9826
11338
|
});
|
|
9827
11339
|
addPlaySearchCommand(play.command("search <query>"));
|
|
9828
|
-
play.command("grep <query>").description(
|
|
11340
|
+
play.command("grep <query>").description(
|
|
11341
|
+
"Literal grep over play names, aliases, schemas, and descriptions."
|
|
11342
|
+
).addHelpText(
|
|
9829
11343
|
"after",
|
|
9830
11344
|
`
|
|
9831
11345
|
Notes:
|
|
@@ -9911,7 +11425,9 @@ Examples:
|
|
|
9911
11425
|
]);
|
|
9912
11426
|
});
|
|
9913
11427
|
addPublishHelp(
|
|
9914
|
-
play.command("set-live <target>").description(
|
|
11428
|
+
play.command("set-live <target>").description(
|
|
11429
|
+
"Promote a local file or saved revision as the live play revision."
|
|
11430
|
+
)
|
|
9915
11431
|
).option("--latest", "Promote the newest saved revision").option("--revision-id <id>", "Revision to promote").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (target, options) => {
|
|
9916
11432
|
process.exitCode = await handlePlayPublish([
|
|
9917
11433
|
target,
|
|
@@ -9956,7 +11472,9 @@ Examples:
|
|
|
9956
11472
|
deepline runs export play/my-play/run/20260501t000000-000 --out output.csv
|
|
9957
11473
|
`
|
|
9958
11474
|
);
|
|
9959
|
-
runs.command("get <runId>").description(
|
|
11475
|
+
runs.command("get <runId>").description(
|
|
11476
|
+
"Get status, progress, result, errors, and recovery metadata for a play run."
|
|
11477
|
+
).addHelpText(
|
|
9960
11478
|
"after",
|
|
9961
11479
|
`
|
|
9962
11480
|
Notes:
|
|
@@ -10024,7 +11542,11 @@ Examples:
|
|
|
10024
11542
|
deepline runs logs play/my-play/run/20260501t000000-000 --limit 500
|
|
10025
11543
|
deepline runs logs play/my-play/run/20260501t000000-000 --out run.log --json
|
|
10026
11544
|
`
|
|
10027
|
-
).option(
|
|
11545
|
+
).option(
|
|
11546
|
+
"--limit <count>",
|
|
11547
|
+
"Maximum recent log lines to print without --out",
|
|
11548
|
+
"200"
|
|
11549
|
+
).option("--out <path>", "Write the full persisted log stream to a file").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (runId, options) => {
|
|
10028
11550
|
process.exitCode = await handleRunLogs([
|
|
10029
11551
|
runId,
|
|
10030
11552
|
...options.limit ? ["--limit", options.limit] : [],
|
|
@@ -10065,7 +11587,10 @@ Examples:
|
|
|
10065
11587
|
deepline runs export play/my-play/run/20260501t000000-000 --out output.csv --metadata-out output.meta.json
|
|
10066
11588
|
deepline runs export play/my-play/run/20260501t000000-000 --out output.csv --json
|
|
10067
11589
|
`
|
|
10068
|
-
).requiredOption("--out <path>", "Output CSV path").option(
|
|
11590
|
+
).requiredOption("--out <path>", "Output CSV path").option(
|
|
11591
|
+
"--dataset <path>",
|
|
11592
|
+
"Returned dataset handle path, such as result.rows"
|
|
11593
|
+
).option("--metadata-out <path>", "Write export metadata JSON to a file").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (runId, options) => {
|
|
10069
11594
|
process.exitCode = await handleRunExport([
|
|
10070
11595
|
runId,
|
|
10071
11596
|
...options.dataset ? ["--dataset", options.dataset] : [],
|
|
@@ -10293,11 +11818,14 @@ async function listTools(args) {
|
|
|
10293
11818
|
const client = new DeeplineClient();
|
|
10294
11819
|
const categoryArgIndex = args.findIndex((arg) => arg === "--categories");
|
|
10295
11820
|
const categoryFilter = categoryArgIndex >= 0 ? args[categoryArgIndex + 1] : "";
|
|
11821
|
+
const compact = args.includes("--compact");
|
|
10296
11822
|
const requestedCategories = categoryFilter ? categoryFilter.split(",").map((item) => item.trim()).filter(Boolean) : [];
|
|
10297
11823
|
const items = (await client.listTools({
|
|
10298
11824
|
...categoryFilter ? { categories: categoryFilter } : {}
|
|
10299
11825
|
})).map(toListedTool).filter(
|
|
10300
|
-
(item) => requestedCategories.length === 0 || requestedCategories.some(
|
|
11826
|
+
(item) => requestedCategories.length === 0 || requestedCategories.some(
|
|
11827
|
+
(category) => item.categories.includes(category)
|
|
11828
|
+
)
|
|
10301
11829
|
);
|
|
10302
11830
|
const render = {
|
|
10303
11831
|
sections: [
|
|
@@ -10317,10 +11845,11 @@ async function listTools(args) {
|
|
|
10317
11845
|
}
|
|
10318
11846
|
]
|
|
10319
11847
|
};
|
|
11848
|
+
const outputItems = compact ? items.map(compactTool) : items;
|
|
10320
11849
|
printCommandEnvelope(
|
|
10321
11850
|
{
|
|
10322
|
-
tools:
|
|
10323
|
-
count:
|
|
11851
|
+
tools: outputItems,
|
|
11852
|
+
count: outputItems.length,
|
|
10324
11853
|
filters: {
|
|
10325
11854
|
categories: requestedCategories
|
|
10326
11855
|
},
|
|
@@ -10345,7 +11874,11 @@ async function searchTools(queryInput, options = {}) {
|
|
|
10345
11874
|
searchMode: options.searchMode,
|
|
10346
11875
|
includeSearchDebug: options.includeSearchDebug
|
|
10347
11876
|
});
|
|
10348
|
-
|
|
11877
|
+
const payload = options.compact && Array.isArray(result.tools) ? {
|
|
11878
|
+
...result,
|
|
11879
|
+
tools: result.tools.map(compactTool)
|
|
11880
|
+
} : result;
|
|
11881
|
+
printCommandEnvelope(payload, {
|
|
10349
11882
|
json: options.json || shouldEmitJson()
|
|
10350
11883
|
});
|
|
10351
11884
|
return 0;
|
|
@@ -10364,7 +11897,9 @@ async function grepTools(queryInput, options = {}) {
|
|
|
10364
11897
|
grepMode: mode,
|
|
10365
11898
|
...options.categories ? { categories: options.categories } : {}
|
|
10366
11899
|
})).map(toListedTool).filter(
|
|
10367
|
-
(item) => requestedCategories.length === 0 || requestedCategories.some(
|
|
11900
|
+
(item) => requestedCategories.length === 0 || requestedCategories.some(
|
|
11901
|
+
(category) => item.categories.includes(category)
|
|
11902
|
+
)
|
|
10368
11903
|
).filter(
|
|
10369
11904
|
(item) => matchesGrepQuery(
|
|
10370
11905
|
{
|
|
@@ -10382,10 +11917,11 @@ async function grepTools(queryInput, options = {}) {
|
|
|
10382
11917
|
mode
|
|
10383
11918
|
)
|
|
10384
11919
|
);
|
|
11920
|
+
const outputTools = options.compact ? tools.map(compactTool) : tools;
|
|
10385
11921
|
printCommandEnvelope(
|
|
10386
11922
|
{
|
|
10387
|
-
tools,
|
|
10388
|
-
count:
|
|
11923
|
+
tools: outputTools,
|
|
11924
|
+
count: outputTools.length,
|
|
10389
11925
|
query,
|
|
10390
11926
|
grep: {
|
|
10391
11927
|
mode,
|
|
@@ -10400,6 +11936,19 @@ async function grepTools(queryInput, options = {}) {
|
|
|
10400
11936
|
);
|
|
10401
11937
|
return 0;
|
|
10402
11938
|
}
|
|
11939
|
+
function compactTool(tool) {
|
|
11940
|
+
const listed = toListedTool(tool);
|
|
11941
|
+
return {
|
|
11942
|
+
id: listed.id,
|
|
11943
|
+
toolId: listed.toolId,
|
|
11944
|
+
provider: listed.provider,
|
|
11945
|
+
displayName: listed.displayName,
|
|
11946
|
+
description: listed.description,
|
|
11947
|
+
categories: listed.categories,
|
|
11948
|
+
type: listed.type,
|
|
11949
|
+
executeCommand: listed.executeCommand
|
|
11950
|
+
};
|
|
11951
|
+
}
|
|
10403
11952
|
function playIdentifiers(play) {
|
|
10404
11953
|
return [play.name, play.reference, ...play.aliases ?? []].filter((value) => Boolean(value?.trim())).map((value) => value.trim());
|
|
10405
11954
|
}
|
|
@@ -10472,9 +12021,13 @@ Examples:
|
|
|
10472
12021
|
deepline tools list --categories email_finder --json
|
|
10473
12022
|
deepline tools search email --json
|
|
10474
12023
|
`
|
|
10475
|
-
).option(
|
|
12024
|
+
).option(
|
|
12025
|
+
"--categories <categories>",
|
|
12026
|
+
"Comma-separated categories to filter inventory"
|
|
12027
|
+
).option("--compact", "Drop verbose fields from JSON output").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (options) => {
|
|
10476
12028
|
process.exitCode = await listTools([
|
|
10477
12029
|
...options.categories ? ["--categories", options.categories] : [],
|
|
12030
|
+
...options.compact ? ["--compact"] : [],
|
|
10478
12031
|
...options.json ? ["--json"] : []
|
|
10479
12032
|
]);
|
|
10480
12033
|
});
|
|
@@ -10492,9 +12045,19 @@ Examples:
|
|
|
10492
12045
|
deepline tools grep "company enrichment" --categories enrichment --json
|
|
10493
12046
|
deepline tools search verifier --search-mode v2 --json
|
|
10494
12047
|
`
|
|
10495
|
-
).option(
|
|
12048
|
+
).option(
|
|
12049
|
+
"--categories <categories>",
|
|
12050
|
+
"Comma-separated categories to filter ranked search"
|
|
12051
|
+
).option(
|
|
12052
|
+
"--search_terms <terms>",
|
|
12053
|
+
"Structured search terms for ranked search"
|
|
12054
|
+
).option(
|
|
12055
|
+
"--search-terms <terms>",
|
|
12056
|
+
"Structured search terms for ranked search"
|
|
12057
|
+
).option("--search-mode <mode>", "Ranked search mode: v1 or v2").option("--include-search-debug", "Include ranked search debug metadata").option("--compact", "Drop verbose fields from JSON output").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (query, options) => {
|
|
10496
12058
|
process.exitCode = await searchTools(query, {
|
|
10497
12059
|
json: options.json,
|
|
12060
|
+
compact: options.compact,
|
|
10498
12061
|
categories: options.categories,
|
|
10499
12062
|
searchTerms: options.searchTerms ?? options.search_terms,
|
|
10500
12063
|
searchMode: options.searchMode === "v1" || options.searchMode === "v2" ? options.searchMode : void 0,
|
|
@@ -10502,7 +12065,9 @@ Examples:
|
|
|
10502
12065
|
});
|
|
10503
12066
|
});
|
|
10504
12067
|
addToolSearchCommand(tools.command("search <query>"));
|
|
10505
|
-
tools.command("grep <query>").description(
|
|
12068
|
+
tools.command("grep <query>").description(
|
|
12069
|
+
"Literal grep over tool ids, descriptions, categories, and input fields."
|
|
12070
|
+
).addHelpText(
|
|
10506
12071
|
"after",
|
|
10507
12072
|
`
|
|
10508
12073
|
Notes:
|
|
@@ -10516,10 +12081,14 @@ Examples:
|
|
|
10516
12081
|
deepline tools grep "phone validate" --mode all --json
|
|
10517
12082
|
deepline tools grep hunter --mode phrase --json
|
|
10518
12083
|
`
|
|
10519
|
-
).option(
|
|
12084
|
+
).option(
|
|
12085
|
+
"--categories <categories>",
|
|
12086
|
+
"Comma-separated categories to filter inventory"
|
|
12087
|
+
).option("--mode <mode>", "Grep matching mode: all, any, or phrase").option("--compact", "Drop verbose fields from JSON output").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (query, options) => {
|
|
10520
12088
|
const mode = options.mode === "any" || options.mode === "phrase" ? options.mode : "all";
|
|
10521
12089
|
process.exitCode = await grepTools(query, {
|
|
10522
12090
|
json: options.json,
|
|
12091
|
+
compact: options.compact,
|
|
10523
12092
|
categories: options.categories,
|
|
10524
12093
|
mode
|
|
10525
12094
|
});
|
|
@@ -10540,7 +12109,20 @@ Examples:
|
|
|
10540
12109
|
deepline tools describe hunter_email_verifier --json
|
|
10541
12110
|
deepline tools execute hunter_email_verifier --input '{"email":"a@b.com"}'
|
|
10542
12111
|
`
|
|
10543
|
-
).option("--json", "Emit JSON output. Also automatic when stdout is piped").option("--pricing-only", "Only print pricing and billing semantics").option("--schema-only", "Only print input schema fields").option(
|
|
12112
|
+
).option("--json", "Emit JSON output. Also automatic when stdout is piped").option("--pricing-only", "Only print pricing and billing semantics").option("--schema-only", "Only print input schema fields").option(
|
|
12113
|
+
"--examples-only",
|
|
12114
|
+
"Only print runnable examples and sample payloads"
|
|
12115
|
+
).option("--getters-only", "Only print extracted list/value getters").addOption(
|
|
12116
|
+
new Option(
|
|
12117
|
+
"--compact",
|
|
12118
|
+
"Compatibility alias for the default compact view"
|
|
12119
|
+
).hideHelp()
|
|
12120
|
+
).addOption(
|
|
12121
|
+
new Option(
|
|
12122
|
+
"--contract-json",
|
|
12123
|
+
"Compatibility alias for compact contract JSON"
|
|
12124
|
+
).hideHelp()
|
|
12125
|
+
).action(async (toolId, options) => {
|
|
10544
12126
|
process.exitCode = await getTool(toolId, {
|
|
10545
12127
|
json: Boolean(options.json),
|
|
10546
12128
|
compact: Boolean(options.compact),
|
|
@@ -10582,13 +12164,24 @@ Examples:
|
|
|
10582
12164
|
deepline tools execute hunter_email_verifier -p email=a@b.com
|
|
10583
12165
|
deepline tools execute test_rate_limit --input '{"key":"smoke"}' --json | jq '.status'
|
|
10584
12166
|
`
|
|
10585
|
-
).option(
|
|
10586
|
-
|
|
10587
|
-
|
|
10588
|
-
|
|
12167
|
+
).option(
|
|
12168
|
+
"-p, --param <key=value>",
|
|
12169
|
+
"Pass one parameter. Repeat for multiple values.",
|
|
12170
|
+
(value, acc) => {
|
|
12171
|
+
acc.push(value);
|
|
12172
|
+
return acc;
|
|
12173
|
+
},
|
|
12174
|
+
[]
|
|
12175
|
+
).option(
|
|
12176
|
+
"--json [payload]",
|
|
12177
|
+
"Emit JSON output. Use `--input` or `--payload` for passing JSON params."
|
|
12178
|
+
).option("--input <payload>", "Merge a JSON object into the tool params").option("--payload <payload>", "Merge a JSON object into the tool params").option(
|
|
10589
12179
|
"--output-format <format>",
|
|
10590
12180
|
"Output format: auto, csv, csv_file, json, or json_file"
|
|
10591
|
-
).option(
|
|
12181
|
+
).option(
|
|
12182
|
+
"--no-preview",
|
|
12183
|
+
"Only print the extracted output path when applicable"
|
|
12184
|
+
).action(async (toolId, options) => {
|
|
10592
12185
|
const args = [
|
|
10593
12186
|
toolId,
|
|
10594
12187
|
...options.param.flatMap((value) => ["--param", value]),
|
|
@@ -10620,14 +12213,18 @@ async function getTool(toolId, options = {}) {
|
|
|
10620
12213
|
throw error;
|
|
10621
12214
|
}
|
|
10622
12215
|
if (options.contractJson) {
|
|
10623
|
-
process.stdout.write(
|
|
10624
|
-
|
|
12216
|
+
process.stdout.write(
|
|
12217
|
+
`${JSON.stringify(toolContractJsonForDescribe(tool, toolId))}
|
|
12218
|
+
`
|
|
12219
|
+
);
|
|
10625
12220
|
return 0;
|
|
10626
12221
|
}
|
|
10627
12222
|
const emitJson = options.json === true;
|
|
10628
12223
|
if (emitJson) {
|
|
10629
|
-
process.stdout.write(
|
|
10630
|
-
|
|
12224
|
+
process.stdout.write(
|
|
12225
|
+
`${JSON.stringify(toolMetadataJsonForDescribe(tool, toolId))}
|
|
12226
|
+
`
|
|
12227
|
+
);
|
|
10631
12228
|
return 0;
|
|
10632
12229
|
}
|
|
10633
12230
|
const onlyModes = [
|
|
@@ -10637,7 +12234,9 @@ async function getTool(toolId, options = {}) {
|
|
|
10637
12234
|
options.gettersOnly
|
|
10638
12235
|
].filter(Boolean).length;
|
|
10639
12236
|
if (onlyModes > 1) {
|
|
10640
|
-
console.error(
|
|
12237
|
+
console.error(
|
|
12238
|
+
"Use only one of --pricing-only, --schema-only, --examples-only, or --getters-only."
|
|
12239
|
+
);
|
|
10641
12240
|
return 2;
|
|
10642
12241
|
}
|
|
10643
12242
|
if (options.pricingOnly) {
|
|
@@ -10661,8 +12260,10 @@ async function getTool(toolId, options = {}) {
|
|
|
10661
12260
|
return 0;
|
|
10662
12261
|
}
|
|
10663
12262
|
if (shouldEmitJson()) {
|
|
10664
|
-
process.stdout.write(
|
|
10665
|
-
|
|
12263
|
+
process.stdout.write(
|
|
12264
|
+
`${JSON.stringify(toolMetadataJsonForDescribe(tool, toolId))}
|
|
12265
|
+
`
|
|
12266
|
+
);
|
|
10666
12267
|
return 0;
|
|
10667
12268
|
}
|
|
10668
12269
|
printCompactToolContract(tool, toolId);
|
|
@@ -10670,9 +12271,15 @@ async function getTool(toolId, options = {}) {
|
|
|
10670
12271
|
}
|
|
10671
12272
|
function toolContractJsonForDescribe(tool, requestedToolId) {
|
|
10672
12273
|
const toolId = String(tool.toolId || requestedToolId);
|
|
10673
|
-
const inputFields = toolInputFieldsForDisplay(
|
|
12274
|
+
const inputFields = toolInputFieldsForDisplay(
|
|
12275
|
+
recordField(tool, "inputSchema", "input_schema")
|
|
12276
|
+
);
|
|
10674
12277
|
const usageGuidance = recordField(tool, "usageGuidance", "usage_guidance");
|
|
10675
|
-
const toolExecutionResult = recordField(
|
|
12278
|
+
const toolExecutionResult = recordField(
|
|
12279
|
+
usageGuidance,
|
|
12280
|
+
"toolExecutionResult",
|
|
12281
|
+
"tool_execution_result"
|
|
12282
|
+
);
|
|
10676
12283
|
const extractedLists = extractionContractEntries(
|
|
10677
12284
|
arrayField(toolExecutionResult, "extractedLists", "extracted_lists")
|
|
10678
12285
|
);
|
|
@@ -10725,7 +12332,7 @@ function toolContractJsonForDescribe(tool, requestedToolId) {
|
|
|
10725
12332
|
}
|
|
10726
12333
|
function extractionContractEntries(entries) {
|
|
10727
12334
|
return entries.flatMap((entry) => {
|
|
10728
|
-
if (!
|
|
12335
|
+
if (!isRecord5(entry)) return [];
|
|
10729
12336
|
const name = stringField(entry, "name");
|
|
10730
12337
|
const expression = stringField(entry, "expression");
|
|
10731
12338
|
return name && expression ? [{ name, expression }] : [];
|
|
@@ -10733,8 +12340,8 @@ function extractionContractEntries(entries) {
|
|
|
10733
12340
|
}
|
|
10734
12341
|
function printCompactToolContract(tool, requestedToolId) {
|
|
10735
12342
|
const contract = toolContractJsonForDescribe(tool, requestedToolId);
|
|
10736
|
-
const cost =
|
|
10737
|
-
const getters =
|
|
12343
|
+
const cost = isRecord5(contract.cost) ? contract.cost : {};
|
|
12344
|
+
const getters = isRecord5(contract.getters) ? contract.getters : {};
|
|
10738
12345
|
const listGetters = Array.isArray(getters.extractedLists) ? getters.extractedLists : [];
|
|
10739
12346
|
const valueGetters = Array.isArray(getters.extractedValues) ? getters.extractedValues : [];
|
|
10740
12347
|
const inputFields = Array.isArray(contract.inputFields) ? contract.inputFields : [];
|
|
@@ -10751,18 +12358,20 @@ function printCompactToolContract(tool, requestedToolId) {
|
|
|
10751
12358
|
console.log("");
|
|
10752
12359
|
console.log("Inputs:");
|
|
10753
12360
|
for (const field of inputFields) {
|
|
10754
|
-
if (!
|
|
12361
|
+
if (!isRecord5(field)) continue;
|
|
10755
12362
|
const name = stringField(field, "name");
|
|
10756
12363
|
if (!name) continue;
|
|
10757
12364
|
const required = field.required ? "*" : "";
|
|
10758
12365
|
const type = stringField(field, "type") || "unknown";
|
|
10759
12366
|
const description = stringField(field, "description");
|
|
10760
|
-
console.log(
|
|
12367
|
+
console.log(
|
|
12368
|
+
`- ${name}${required}: ${type}${description ? ` - ${description}` : ""}`
|
|
12369
|
+
);
|
|
10761
12370
|
}
|
|
10762
12371
|
}
|
|
10763
12372
|
console.log("");
|
|
10764
12373
|
printToolExamplesOnly(tool, requestedToolId, { includeSamples: false });
|
|
10765
|
-
const starterScript =
|
|
12374
|
+
const starterScript = isRecord5(contract.starterScript) ? contract.starterScript : {};
|
|
10766
12375
|
const starterPath = stringField(starterScript, "path");
|
|
10767
12376
|
if (starterPath) {
|
|
10768
12377
|
console.log("");
|
|
@@ -10774,19 +12383,27 @@ function printCompactToolContract(tool, requestedToolId) {
|
|
|
10774
12383
|
console.log("Getters:");
|
|
10775
12384
|
if (listGetters.length) console.log("Lists:");
|
|
10776
12385
|
for (const entry of listGetters) {
|
|
10777
|
-
if (
|
|
12386
|
+
if (isRecord5(entry))
|
|
12387
|
+
console.log(
|
|
12388
|
+
`- ${stringField(entry, "name")}: ${playResultExpression(entry)}`
|
|
12389
|
+
);
|
|
10778
12390
|
}
|
|
10779
12391
|
if (valueGetters.length) console.log("Values:");
|
|
10780
12392
|
for (const entry of valueGetters) {
|
|
10781
|
-
if (
|
|
12393
|
+
if (isRecord5(entry))
|
|
12394
|
+
console.log(
|
|
12395
|
+
`- ${stringField(entry, "name")}: ${playResultExpression(entry)}`
|
|
12396
|
+
);
|
|
10782
12397
|
}
|
|
10783
12398
|
}
|
|
10784
12399
|
console.log("");
|
|
10785
|
-
console.log(
|
|
12400
|
+
console.log(
|
|
12401
|
+
`More: deepline tools describe ${contract.toolId} --pricing-only | --schema-only | --examples-only | --getters-only | --json`
|
|
12402
|
+
);
|
|
10786
12403
|
}
|
|
10787
12404
|
function printToolPricingOnly(tool, requestedToolId, options = {}) {
|
|
10788
12405
|
const contract = toolContractJsonForDescribe(tool, requestedToolId);
|
|
10789
|
-
const cost =
|
|
12406
|
+
const cost = isRecord5(contract.cost) ? contract.cost : {};
|
|
10790
12407
|
const pricingModel = stringField(cost, "pricingModel") || "unknown";
|
|
10791
12408
|
const billingMode = stringField(cost, "billingMode") || "unknown";
|
|
10792
12409
|
const unit = pricingModel === "per_page" ? "page" : pricingModel === "per_result" ? "result" : pricingModel === "fixed" ? "call" : pricingModel.replace(/^per_/, "") || "unit";
|
|
@@ -10806,14 +12423,16 @@ function printToolSchemaOnly(tool, requestedToolId) {
|
|
|
10806
12423
|
}
|
|
10807
12424
|
console.log("Inputs:");
|
|
10808
12425
|
for (const field of inputFields) {
|
|
10809
|
-
if (!
|
|
12426
|
+
if (!isRecord5(field)) continue;
|
|
10810
12427
|
const name = stringField(field, "name");
|
|
10811
12428
|
if (!name) continue;
|
|
10812
12429
|
const required = field.required ? "*" : "";
|
|
10813
12430
|
const type = stringField(field, "type") || "unknown";
|
|
10814
12431
|
const description = stringField(field, "description");
|
|
10815
12432
|
const defaultSuffix = Object.prototype.hasOwnProperty.call(field, "default") ? ` default=${JSON.stringify(field.default)}` : "";
|
|
10816
|
-
console.log(
|
|
12433
|
+
console.log(
|
|
12434
|
+
`- ${name}${required}: ${type}${defaultSuffix}${description ? ` - ${description}` : ""}`
|
|
12435
|
+
);
|
|
10817
12436
|
}
|
|
10818
12437
|
}
|
|
10819
12438
|
function printToolExamplesOnly(tool, requestedToolId, options = {}) {
|
|
@@ -10826,16 +12445,21 @@ function printToolExamplesOnly(tool, requestedToolId, options = {}) {
|
|
|
10826
12445
|
console.log("const result = await ctx.tools.execute({");
|
|
10827
12446
|
console.log(` id: '${stableStepIdForTool(toolId)}',`);
|
|
10828
12447
|
console.log(` tool: '${toolId}',`);
|
|
10829
|
-
console.log(
|
|
12448
|
+
console.log(
|
|
12449
|
+
` input: ${JSON.stringify(sampleInput || {}, null, 2).replace(/\n/g, "\n ")},`
|
|
12450
|
+
);
|
|
10830
12451
|
console.log("});");
|
|
10831
|
-
const getters =
|
|
12452
|
+
const getters = isRecord5(contract.getters) ? contract.getters : {};
|
|
10832
12453
|
const valueGetters = Array.isArray(getters.extractedValues) ? getters.extractedValues : [];
|
|
10833
12454
|
const listGetters = Array.isArray(getters.extractedLists) ? getters.extractedLists : [];
|
|
10834
|
-
const firstGetter = [...valueGetters, ...listGetters].find(
|
|
12455
|
+
const firstGetter = [...valueGetters, ...listGetters].find(isRecord5);
|
|
10835
12456
|
if (firstGetter) {
|
|
10836
12457
|
const name = stringField(firstGetter, "name") || "value";
|
|
10837
12458
|
const expression = stringField(firstGetter, "expression");
|
|
10838
|
-
if (expression)
|
|
12459
|
+
if (expression)
|
|
12460
|
+
console.log(
|
|
12461
|
+
`const ${safeIdentifier2(name)} = ${expression.replace(/^toolExecutionResult\./, "result.")};`
|
|
12462
|
+
);
|
|
10839
12463
|
}
|
|
10840
12464
|
console.log("```");
|
|
10841
12465
|
if (options.includeSamples !== false) {
|
|
@@ -10845,31 +12469,40 @@ function printToolExamplesOnly(tool, requestedToolId, options = {}) {
|
|
|
10845
12469
|
}
|
|
10846
12470
|
function printToolGettersOnly(tool, requestedToolId) {
|
|
10847
12471
|
const contract = toolContractJsonForDescribe(tool, requestedToolId);
|
|
10848
|
-
const getters =
|
|
12472
|
+
const getters = isRecord5(contract.getters) ? contract.getters : {};
|
|
10849
12473
|
const listGetters = Array.isArray(getters.extractedLists) ? getters.extractedLists : [];
|
|
10850
12474
|
const valueGetters = Array.isArray(getters.extractedValues) ? getters.extractedValues : [];
|
|
10851
12475
|
console.log(`Getters: ${contract.toolId}`);
|
|
10852
12476
|
if (!listGetters.length && !valueGetters.length) {
|
|
10853
|
-
console.log(
|
|
12477
|
+
console.log(
|
|
12478
|
+
"No generated getters declared. Use --json only if you need raw metadata."
|
|
12479
|
+
);
|
|
10854
12480
|
return;
|
|
10855
12481
|
}
|
|
10856
12482
|
if (listGetters.length) {
|
|
10857
12483
|
console.log("Lists:");
|
|
10858
12484
|
for (const entry of listGetters) {
|
|
10859
|
-
if (
|
|
12485
|
+
if (isRecord5(entry))
|
|
12486
|
+
console.log(
|
|
12487
|
+
`- ${stringField(entry, "name")}: ${playResultExpression(entry)}`
|
|
12488
|
+
);
|
|
10860
12489
|
}
|
|
10861
12490
|
}
|
|
10862
12491
|
if (valueGetters.length) {
|
|
10863
12492
|
console.log("Values:");
|
|
10864
12493
|
for (const entry of valueGetters) {
|
|
10865
|
-
if (
|
|
12494
|
+
if (isRecord5(entry))
|
|
12495
|
+
console.log(
|
|
12496
|
+
`- ${stringField(entry, "name")}: ${playResultExpression(entry)}`
|
|
12497
|
+
);
|
|
10866
12498
|
}
|
|
10867
12499
|
}
|
|
10868
12500
|
}
|
|
10869
12501
|
function sampleValueForField(field) {
|
|
10870
12502
|
const name = stringField(field, "name").toLowerCase();
|
|
10871
12503
|
const type = stringField(field, "type").toLowerCase();
|
|
10872
|
-
if (Object.prototype.hasOwnProperty.call(field, "default"))
|
|
12504
|
+
if (Object.prototype.hasOwnProperty.call(field, "default"))
|
|
12505
|
+
return field.default;
|
|
10873
12506
|
if (name.includes("email")) return "ada@example.com";
|
|
10874
12507
|
if (name.includes("domain") || name.includes("website")) return "example.com";
|
|
10875
12508
|
if (name.includes("first")) return "Ada";
|
|
@@ -10884,7 +12517,7 @@ function sampleValueForField(field) {
|
|
|
10884
12517
|
function samplePayloadForInputFields(fields) {
|
|
10885
12518
|
return Object.fromEntries(
|
|
10886
12519
|
fields.slice(0, 4).flatMap((field) => {
|
|
10887
|
-
if (!
|
|
12520
|
+
if (!isRecord5(field)) return [];
|
|
10888
12521
|
const name = stringField(field, "name");
|
|
10889
12522
|
if (!name) return [];
|
|
10890
12523
|
return [[name, sampleValueForField(field)]];
|
|
@@ -10894,12 +12527,15 @@ function samplePayloadForInputFields(fields) {
|
|
|
10894
12527
|
function stableStepIdForTool(toolId) {
|
|
10895
12528
|
return toolId.replace(/^[a-z0-9]+_/, "").replace(/[^a-z0-9_]+/gi, "_") || "tool_call";
|
|
10896
12529
|
}
|
|
10897
|
-
function
|
|
12530
|
+
function safeIdentifier2(name) {
|
|
10898
12531
|
const cleaned = name.replace(/[^a-zA-Z0-9_$]+/g, "_").replace(/^[^a-zA-Z_$]+/, "");
|
|
10899
12532
|
return cleaned || "value";
|
|
10900
12533
|
}
|
|
10901
12534
|
function playResultExpression(entry) {
|
|
10902
|
-
return stringField(entry, "expression").replace(
|
|
12535
|
+
return stringField(entry, "expression").replace(
|
|
12536
|
+
/^toolExecutionResult\./,
|
|
12537
|
+
"result."
|
|
12538
|
+
);
|
|
10903
12539
|
}
|
|
10904
12540
|
function toolMetadataJsonForDescribe(tool, requestedToolId) {
|
|
10905
12541
|
const toolId = String(tool.toolId || requestedToolId);
|
|
@@ -10990,12 +12626,13 @@ function formatListedToolCost(tool) {
|
|
|
10990
12626
|
return displayText ? `Cost: ${displayText}` : "";
|
|
10991
12627
|
}
|
|
10992
12628
|
function toolInputFieldsForDisplay(inputSchema) {
|
|
10993
|
-
if (Array.isArray(inputSchema.fields))
|
|
10994
|
-
|
|
10995
|
-
const
|
|
12629
|
+
if (Array.isArray(inputSchema.fields))
|
|
12630
|
+
return inputSchema.fields.filter(isRecord5);
|
|
12631
|
+
const jsonSchema = isRecord5(inputSchema.jsonSchema) ? inputSchema.jsonSchema : inputSchema;
|
|
12632
|
+
const properties = isRecord5(jsonSchema.properties) ? jsonSchema.properties : {};
|
|
10996
12633
|
const required = Array.isArray(jsonSchema.required) ? new Set(jsonSchema.required.map(String)) : /* @__PURE__ */ new Set();
|
|
10997
12634
|
return Object.entries(properties).map(([name, value]) => {
|
|
10998
|
-
const property =
|
|
12635
|
+
const property = isRecord5(value) ? value : {};
|
|
10999
12636
|
return {
|
|
11000
12637
|
name,
|
|
11001
12638
|
type: typeof property.type === "string" ? property.type : "unknown",
|
|
@@ -11010,8 +12647,10 @@ function printSamples(samples) {
|
|
|
11010
12647
|
const responsePayload = samplePayload(samples, "response");
|
|
11011
12648
|
if (requestPayload === void 0 && responsePayload === void 0) return;
|
|
11012
12649
|
console.log(" Samples:");
|
|
11013
|
-
if (requestPayload !== void 0)
|
|
11014
|
-
|
|
12650
|
+
if (requestPayload !== void 0)
|
|
12651
|
+
printJsonPreview("Example payload", requestPayload);
|
|
12652
|
+
if (responsePayload !== void 0)
|
|
12653
|
+
printJsonPreview("Example response", responsePayload);
|
|
11015
12654
|
}
|
|
11016
12655
|
function printJsonPreview(label, payload) {
|
|
11017
12656
|
console.log(` ${label}:`);
|
|
@@ -11022,15 +12661,15 @@ function printJsonPreview(label, payload) {
|
|
|
11022
12661
|
}
|
|
11023
12662
|
function samplePayload(samples, key) {
|
|
11024
12663
|
const entry = samples[key];
|
|
11025
|
-
if (!
|
|
12664
|
+
if (!isRecord5(entry)) return void 0;
|
|
11026
12665
|
return Object.prototype.hasOwnProperty.call(entry, "payload") ? entry.payload : entry;
|
|
11027
12666
|
}
|
|
11028
12667
|
function commandEnvelopeFromRawResponse(rawResponse) {
|
|
11029
|
-
return
|
|
12668
|
+
return isRecord5(rawResponse) ? { ...rawResponse } : { status: "completed", result: rawResponse };
|
|
11030
12669
|
}
|
|
11031
12670
|
function listExtractorPathsFromUsageGuidance(tool) {
|
|
11032
12671
|
const toolExecutionResult = tool.usageGuidance?.toolExecutionResult;
|
|
11033
|
-
const extractedLists = Array.isArray(toolExecutionResult?.extractedLists) ? toolExecutionResult.extractedLists :
|
|
12672
|
+
const extractedLists = Array.isArray(toolExecutionResult?.extractedLists) ? toolExecutionResult.extractedLists : isRecord5(toolExecutionResult?.extractedLists) ? Object.values(toolExecutionResult.extractedLists) : [];
|
|
11034
12673
|
return extractedLists.flatMap((entry) => {
|
|
11035
12674
|
const paths = entry.details?.candidatePaths ?? entry.details?.rawToolOutputPaths;
|
|
11036
12675
|
if (!Array.isArray(paths)) return [];
|
|
@@ -11046,7 +12685,7 @@ function formatDecimal(value) {
|
|
|
11046
12685
|
function formatUsd(value) {
|
|
11047
12686
|
return `$${formatDecimal(value)}`;
|
|
11048
12687
|
}
|
|
11049
|
-
function
|
|
12688
|
+
function isRecord5(value) {
|
|
11050
12689
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
11051
12690
|
}
|
|
11052
12691
|
function stringField(source, ...keys) {
|
|
@@ -11073,7 +12712,7 @@ function arrayField(source, ...keys) {
|
|
|
11073
12712
|
function recordField(source, ...keys) {
|
|
11074
12713
|
for (const key of keys) {
|
|
11075
12714
|
const value = source[key];
|
|
11076
|
-
if (
|
|
12715
|
+
if (isRecord5(value)) return value;
|
|
11077
12716
|
}
|
|
11078
12717
|
return {};
|
|
11079
12718
|
}
|
|
@@ -11096,7 +12735,9 @@ function normalizeOutputFormat(raw) {
|
|
|
11096
12735
|
function parseExecuteOptions(args) {
|
|
11097
12736
|
const toolId = args[0];
|
|
11098
12737
|
if (!toolId) {
|
|
11099
|
-
throw new Error(
|
|
12738
|
+
throw new Error(
|
|
12739
|
+
`Usage: deepline tools execute <toolId> [--param key=value ...] [--input '{"k":"v"}'] [--output-format auto|csv|csv_file|json|json_file] [--no-preview]`
|
|
12740
|
+
);
|
|
11100
12741
|
}
|
|
11101
12742
|
const params = {};
|
|
11102
12743
|
let outputFormat = "auto";
|
|
@@ -11206,7 +12847,7 @@ function buildToolExecuteBaseEnvelope(input) {
|
|
|
11206
12847
|
kind: summaryEntries.length > 0 ? "object" : "raw",
|
|
11207
12848
|
summary: input.summary
|
|
11208
12849
|
};
|
|
11209
|
-
const envelopeHasCanonicalOutput =
|
|
12850
|
+
const envelopeHasCanonicalOutput = isRecord5(envelope.toolResponse) && Object.prototype.hasOwnProperty.call(envelope.toolResponse, "raw");
|
|
11210
12851
|
const inspectCommand = `deepline tools execute ${input.toolId} --input ${shellQuote(JSON.stringify(input.params))} --json`;
|
|
11211
12852
|
const actions = input.listConversion ? [
|
|
11212
12853
|
{
|
|
@@ -11240,7 +12881,9 @@ function buildToolExecuteBaseEnvelope(input) {
|
|
|
11240
12881
|
] : [
|
|
11241
12882
|
{
|
|
11242
12883
|
title: "result",
|
|
11243
|
-
lines: summaryEntries.length > 0 ? summaryEntries.map(
|
|
12884
|
+
lines: summaryEntries.length > 0 ? summaryEntries.map(
|
|
12885
|
+
([key, value]) => `${key}=${String(value)}`
|
|
12886
|
+
) : [JSON.stringify(input.rawResponse, null, 2)]
|
|
11244
12887
|
}
|
|
11245
12888
|
],
|
|
11246
12889
|
actions
|
|
@@ -11267,7 +12910,9 @@ async function executeTool(args) {
|
|
|
11267
12910
|
const play = await findPlayForToolId(client, parsed.toolId);
|
|
11268
12911
|
if (play) {
|
|
11269
12912
|
if (argsWantJson(args)) {
|
|
11270
|
-
printJsonError(
|
|
12913
|
+
printJsonError(
|
|
12914
|
+
new Error(playAliasToolErrorMessage(parsed.toolId, play))
|
|
12915
|
+
);
|
|
11271
12916
|
} else {
|
|
11272
12917
|
printPlayAliasToolError(parsed.toolId, play);
|
|
11273
12918
|
}
|
|
@@ -11303,12 +12948,15 @@ async function executeTool(args) {
|
|
|
11303
12948
|
return 0;
|
|
11304
12949
|
}
|
|
11305
12950
|
if (parsed.outputFormat === "json_file") {
|
|
11306
|
-
const jsonPath = writeJsonOutputFile(
|
|
12951
|
+
const jsonPath = writeJsonOutputFile(
|
|
12952
|
+
rawResponse,
|
|
12953
|
+
`payload_${parsed.toolId}`
|
|
12954
|
+
);
|
|
11307
12955
|
printCommandEnvelope(
|
|
11308
12956
|
{
|
|
11309
12957
|
...baseEnvelope,
|
|
11310
12958
|
local: {
|
|
11311
|
-
...
|
|
12959
|
+
...isRecord5(baseEnvelope.local) ? baseEnvelope.local : {},
|
|
11312
12960
|
payload_file: jsonPath
|
|
11313
12961
|
}
|
|
11314
12962
|
},
|
|
@@ -11318,7 +12966,10 @@ async function executeTool(args) {
|
|
|
11318
12966
|
}
|
|
11319
12967
|
if (!listConversion) {
|
|
11320
12968
|
if (parsed.outputFormat === "csv" || parsed.outputFormat === "csv_file") {
|
|
11321
|
-
const jsonPath = writeJsonOutputFile(
|
|
12969
|
+
const jsonPath = writeJsonOutputFile(
|
|
12970
|
+
rawResponse,
|
|
12971
|
+
`payload_${parsed.toolId}`
|
|
12972
|
+
);
|
|
11322
12973
|
printCommandEnvelope(
|
|
11323
12974
|
{
|
|
11324
12975
|
...baseEnvelope,
|
|
@@ -11333,7 +12984,10 @@ async function executeTool(args) {
|
|
|
11333
12984
|
printCommandEnvelope(baseEnvelope, { json: false });
|
|
11334
12985
|
return 0;
|
|
11335
12986
|
}
|
|
11336
|
-
const csv = writeCsvOutputFile(
|
|
12987
|
+
const csv = writeCsvOutputFile(
|
|
12988
|
+
listConversion.rows,
|
|
12989
|
+
`${parsed.toolId}_output`
|
|
12990
|
+
);
|
|
11337
12991
|
const seededScript = seedToolListScript({
|
|
11338
12992
|
toolId: parsed.toolId,
|
|
11339
12993
|
payload: parsed.params,
|
|
@@ -11359,7 +13013,9 @@ async function executeTool(args) {
|
|
|
11359
13013
|
title: `${csv.path} (${csv.rowCount} rows)`,
|
|
11360
13014
|
lines: [
|
|
11361
13015
|
...csv.columns.length > 0 ? [`columns: ${JSON.stringify(csv.columns)}`] : [],
|
|
11362
|
-
...Object.keys(summary).length > 0 ? [
|
|
13016
|
+
...Object.keys(summary).length > 0 ? [
|
|
13017
|
+
`summary: ${Object.entries(summary).map(([key, value]) => `${key}=${String(value)}`).join(", ")}`
|
|
13018
|
+
] : [],
|
|
11363
13019
|
`preview: ${JSON.stringify(csv.preview)}`,
|
|
11364
13020
|
`starter script: ${seededScript.path}`
|
|
11365
13021
|
]
|
|
@@ -11382,22 +13038,25 @@ async function executeTool(args) {
|
|
|
11382
13038
|
}
|
|
11383
13039
|
};
|
|
11384
13040
|
if (parsed.outputFormat === "csv_file") {
|
|
11385
|
-
printCommandEnvelope(
|
|
11386
|
-
|
|
11387
|
-
|
|
11388
|
-
|
|
11389
|
-
|
|
11390
|
-
|
|
11391
|
-
|
|
11392
|
-
|
|
11393
|
-
|
|
11394
|
-
|
|
11395
|
-
|
|
11396
|
-
|
|
11397
|
-
|
|
13041
|
+
printCommandEnvelope(
|
|
13042
|
+
{
|
|
13043
|
+
...materializedEnvelope,
|
|
13044
|
+
extracted_csv: csv.path,
|
|
13045
|
+
extracted_csv_rows: csv.rowCount,
|
|
13046
|
+
extracted_csv_columns: csv.columns,
|
|
13047
|
+
preview: csv.preview,
|
|
13048
|
+
list_strategy: listConversion.strategy,
|
|
13049
|
+
list_source_path: listConversion.sourcePath,
|
|
13050
|
+
starter_script: seededScript.path,
|
|
13051
|
+
project_dir: seededScript.projectDir,
|
|
13052
|
+
copy_to_project: {
|
|
13053
|
+
macos_linux: seededScript.macCopyCommand,
|
|
13054
|
+
windows_powershell: seededScript.windowsCopyCommand
|
|
13055
|
+
},
|
|
13056
|
+
summary
|
|
11398
13057
|
},
|
|
11399
|
-
|
|
11400
|
-
|
|
13058
|
+
{ json: true }
|
|
13059
|
+
);
|
|
11401
13060
|
return 0;
|
|
11402
13061
|
}
|
|
11403
13062
|
if (parsed.noPreview) {
|
|
@@ -11421,7 +13080,7 @@ async function executeTool(args) {
|
|
|
11421
13080
|
// src/cli/commands/update.ts
|
|
11422
13081
|
import { spawn } from "child_process";
|
|
11423
13082
|
import { existsSync as existsSync7 } from "fs";
|
|
11424
|
-
import { dirname as dirname9, join as join9, resolve as
|
|
13083
|
+
import { dirname as dirname9, join as join9, resolve as resolve11 } from "path";
|
|
11425
13084
|
function posixShellQuote(value) {
|
|
11426
13085
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
11427
13086
|
}
|
|
@@ -11440,7 +13099,7 @@ function buildSourceUpdateCommand(sourceRoot) {
|
|
|
11440
13099
|
return `${cdCommand} && git fetch origin main --tags && git merge --ff-only origin/main`;
|
|
11441
13100
|
}
|
|
11442
13101
|
function findRepoBackedSdkRoot(startPath) {
|
|
11443
|
-
let current =
|
|
13102
|
+
let current = resolve11(startPath);
|
|
11444
13103
|
while (true) {
|
|
11445
13104
|
if (existsSync7(join9(current, "sdk", "package.json")) && existsSync7(join9(current, "sdk", "bin", "deepline-dev.ts"))) {
|
|
11446
13105
|
return current;
|
|
@@ -11451,7 +13110,7 @@ function findRepoBackedSdkRoot(startPath) {
|
|
|
11451
13110
|
}
|
|
11452
13111
|
}
|
|
11453
13112
|
function resolveUpdatePlan() {
|
|
11454
|
-
const entrypoint = process.argv[1] ?
|
|
13113
|
+
const entrypoint = process.argv[1] ? resolve11(process.argv[1]) : "";
|
|
11455
13114
|
const sourceRoot = entrypoint ? findRepoBackedSdkRoot(dirname9(entrypoint)) : null;
|
|
11456
13115
|
if (sourceRoot) {
|
|
11457
13116
|
return {
|
|
@@ -11545,7 +13204,7 @@ Examples:
|
|
|
11545
13204
|
|
|
11546
13205
|
// src/cli/skills-sync.ts
|
|
11547
13206
|
import { spawn as spawn2, spawnSync } from "child_process";
|
|
11548
|
-
import { existsSync as existsSync8, mkdirSync as mkdirSync5, readdirSync as readdirSync2, readFileSync as readFileSync6, statSync, writeFileSync as writeFileSync9 } from "fs";
|
|
13207
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync5, readdirSync as readdirSync2, readFileSync as readFileSync6, statSync as statSync2, writeFileSync as writeFileSync9 } from "fs";
|
|
11549
13208
|
import { homedir as homedir4 } from "os";
|
|
11550
13209
|
import { dirname as dirname10, join as join10 } from "path";
|
|
11551
13210
|
var CHECK_TIMEOUT_MS2 = 3e3;
|
|
@@ -11591,7 +13250,7 @@ function installedSdkSkillHasStalePositionalExecuteExamples() {
|
|
|
11591
13250
|
const scan = (dir) => {
|
|
11592
13251
|
for (const entry of readdirSync2(dir)) {
|
|
11593
13252
|
const path = join10(dir, entry);
|
|
11594
|
-
const stat3 =
|
|
13253
|
+
const stat3 = statSync2(path);
|
|
11595
13254
|
if (stat3.isDirectory()) {
|
|
11596
13255
|
if (scan(path)) return true;
|
|
11597
13256
|
continue;
|
|
@@ -11702,7 +13361,7 @@ function resolveSkillsInstallCommands(baseUrl) {
|
|
|
11702
13361
|
return [npxInstall];
|
|
11703
13362
|
}
|
|
11704
13363
|
function runOneSkillsInstall(install) {
|
|
11705
|
-
return new Promise((
|
|
13364
|
+
return new Promise((resolve12) => {
|
|
11706
13365
|
const child = spawn2(install.command, install.args, {
|
|
11707
13366
|
stdio: ["ignore", "ignore", "pipe"],
|
|
11708
13367
|
env: process.env
|
|
@@ -11712,7 +13371,7 @@ function runOneSkillsInstall(install) {
|
|
|
11712
13371
|
stderr += chunk.toString("utf-8");
|
|
11713
13372
|
});
|
|
11714
13373
|
child.on("error", (error) => {
|
|
11715
|
-
|
|
13374
|
+
resolve12({
|
|
11716
13375
|
ok: false,
|
|
11717
13376
|
detail: `failed to start ${install.command}: ${error.message}`,
|
|
11718
13377
|
manualCommand: install.manualCommand
|
|
@@ -11720,11 +13379,11 @@ function runOneSkillsInstall(install) {
|
|
|
11720
13379
|
});
|
|
11721
13380
|
child.on("close", (code) => {
|
|
11722
13381
|
if (code === 0) {
|
|
11723
|
-
|
|
13382
|
+
resolve12({ ok: true, detail: "", manualCommand: install.manualCommand });
|
|
11724
13383
|
return;
|
|
11725
13384
|
}
|
|
11726
13385
|
const detail = stderr.trim();
|
|
11727
|
-
|
|
13386
|
+
resolve12({
|
|
11728
13387
|
ok: false,
|
|
11729
13388
|
detail: detail ? `${install.command}: ${detail}` : `${install.command} exited ${code}`,
|
|
11730
13389
|
manualCommand: install.manualCommand
|