deepline 0.1.93 → 0.1.95
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 +2702 -509
- package/dist/cli/index.mjs +2725 -525
- package/dist/index.d.mts +158 -103
- package/dist/index.d.ts +158 -103
- package/dist/index.js +129 -39
- package/dist/index.mjs +129 -39
- package/dist/repo/apps/play-runner-workers/src/entry.ts +23 -8
- package/dist/repo/sdk/src/client.ts +123 -0
- package/dist/repo/sdk/src/play.ts +51 -0
- package/dist/repo/sdk/src/release.ts +2 -2
- package/dist/repo/shared_libs/play-runtime/email-status.ts +10 -36
- package/dist/repo/shared_libs/play-runtime/extractor-targets.ts +3 -3
- package/dist/repo/shared_libs/play-runtime/tool-result.ts +44 -0
- package/dist/repo/shared_libs/plays/secret-guardrails.ts +22 -11
- package/package.json +1 -1
|
@@ -73,6 +73,10 @@
|
|
|
73
73
|
import { DeeplineClient } from './client.js';
|
|
74
74
|
import { DeeplineError } from './errors.js';
|
|
75
75
|
import { createToolExecuteResult } from '../../shared_libs/play-runtime/tool-result.js';
|
|
76
|
+
export {
|
|
77
|
+
readValue,
|
|
78
|
+
readList,
|
|
79
|
+
} from '../../shared_libs/play-runtime/tool-result.js';
|
|
76
80
|
import type {
|
|
77
81
|
PlayDataset,
|
|
78
82
|
PlayDatasetInput,
|
|
@@ -81,6 +85,7 @@ import type {
|
|
|
81
85
|
ToolExecuteResult,
|
|
82
86
|
ToolResultMetadataInput,
|
|
83
87
|
} from '../../shared_libs/play-runtime/tool-result-types.js';
|
|
88
|
+
import type { EmailStatusExtractorConfig } from '../../shared_libs/play-runtime/email-status.js';
|
|
84
89
|
import type { PreviousCell } from '../../shared_libs/play-runtime/cell-staleness.js';
|
|
85
90
|
import type {
|
|
86
91
|
DeeplineClientOptions,
|
|
@@ -1549,6 +1554,50 @@ function stringArray(value: unknown): string[] {
|
|
|
1549
1554
|
return Array.isArray(value) ? value.map(String) : [];
|
|
1550
1555
|
}
|
|
1551
1556
|
|
|
1557
|
+
function emailStatusExtractorConfig(
|
|
1558
|
+
value: unknown,
|
|
1559
|
+
): EmailStatusExtractorConfig | undefined {
|
|
1560
|
+
if (!isRecord(value)) return undefined;
|
|
1561
|
+
const readPaths = (key: string): string[] | undefined => {
|
|
1562
|
+
const paths = stringArray(value[key])
|
|
1563
|
+
.map((path) => path.trim())
|
|
1564
|
+
.filter(Boolean);
|
|
1565
|
+
return paths.length > 0 ? paths : undefined;
|
|
1566
|
+
};
|
|
1567
|
+
const provider =
|
|
1568
|
+
typeof value.provider === 'string' && value.provider.trim()
|
|
1569
|
+
? value.provider.trim()
|
|
1570
|
+
: null;
|
|
1571
|
+
if (!provider) return undefined;
|
|
1572
|
+
const config: EmailStatusExtractorConfig = { provider };
|
|
1573
|
+
for (const key of [
|
|
1574
|
+
'rawStatus',
|
|
1575
|
+
'rawScore',
|
|
1576
|
+
'valid',
|
|
1577
|
+
'deliverability',
|
|
1578
|
+
'catchAll',
|
|
1579
|
+
'mxProvider',
|
|
1580
|
+
'mxRecord',
|
|
1581
|
+
'fraudScore',
|
|
1582
|
+
'disposable',
|
|
1583
|
+
'roleBased',
|
|
1584
|
+
'freeEmail',
|
|
1585
|
+
'abuse',
|
|
1586
|
+
'spamtrap',
|
|
1587
|
+
'suspect',
|
|
1588
|
+
] as const) {
|
|
1589
|
+
const paths = readPaths(key);
|
|
1590
|
+
if (paths) config[key] = paths;
|
|
1591
|
+
}
|
|
1592
|
+
if (isRecord(value.statusMap)) {
|
|
1593
|
+
config.statusMap = value.statusMap as EmailStatusExtractorConfig['statusMap'];
|
|
1594
|
+
}
|
|
1595
|
+
if (Array.isArray(value.rules)) {
|
|
1596
|
+
config.rules = value.rules as EmailStatusExtractorConfig['rules'];
|
|
1597
|
+
}
|
|
1598
|
+
return config;
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1552
1601
|
function extractorDescriptorRecord(
|
|
1553
1602
|
value: unknown,
|
|
1554
1603
|
): ToolResultMetadataInput['extractors'] {
|
|
@@ -1566,6 +1615,7 @@ function extractorDescriptorRecord(
|
|
|
1566
1615
|
const enumValues = stringArray(descriptor.enum)
|
|
1567
1616
|
.map((entry) => entry.trim())
|
|
1568
1617
|
.filter(Boolean);
|
|
1618
|
+
const emailStatus = emailStatusExtractorConfig(descriptor.emailStatus);
|
|
1569
1619
|
return [
|
|
1570
1620
|
[
|
|
1571
1621
|
key,
|
|
@@ -1573,6 +1623,7 @@ function extractorDescriptorRecord(
|
|
|
1573
1623
|
paths,
|
|
1574
1624
|
...(transforms.length > 0 ? { transforms } : {}),
|
|
1575
1625
|
...(enumValues.length > 0 ? { enum: enumValues } : {}),
|
|
1626
|
+
...(emailStatus ? { emailStatus } : {}),
|
|
1576
1627
|
},
|
|
1577
1628
|
],
|
|
1578
1629
|
];
|
|
@@ -50,10 +50,10 @@ export type SdkRelease = {
|
|
|
50
50
|
};
|
|
51
51
|
|
|
52
52
|
export const SDK_RELEASE = {
|
|
53
|
-
version: '0.1.
|
|
53
|
+
version: '0.1.95',
|
|
54
54
|
apiContract: '2026-06-dataset-column-cell-stale-hard-cutover',
|
|
55
55
|
supportPolicy: {
|
|
56
|
-
latest: '0.1.
|
|
56
|
+
latest: '0.1.95',
|
|
57
57
|
minimumSupported: '0.1.53',
|
|
58
58
|
deprecatedBelow: '0.1.53',
|
|
59
59
|
},
|
|
@@ -89,41 +89,15 @@ export type EmailStatusBuildInput = {
|
|
|
89
89
|
values: Record<string, unknown>;
|
|
90
90
|
};
|
|
91
91
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
status: 'catch_all',
|
|
102
|
-
verdict: 'verify_next',
|
|
103
|
-
verified: false,
|
|
104
|
-
},
|
|
105
|
-
catch_all: {
|
|
106
|
-
status: 'catch_all',
|
|
107
|
-
verdict: 'verify_next',
|
|
108
|
-
verified: false,
|
|
109
|
-
},
|
|
110
|
-
valid_catch_all: {
|
|
111
|
-
status: 'valid_catch_all',
|
|
112
|
-
verdict: 'send_with_caution',
|
|
113
|
-
verified: true,
|
|
114
|
-
},
|
|
115
|
-
accept_all: {
|
|
116
|
-
status: 'catch_all',
|
|
117
|
-
verdict: 'verify_next',
|
|
118
|
-
verified: false,
|
|
119
|
-
},
|
|
120
|
-
unknown: { status: 'unknown', verdict: 'hold', verified: false },
|
|
121
|
-
unavailable: { status: 'unknown', verdict: 'hold', verified: false },
|
|
122
|
-
do_not_mail: { status: 'do_not_mail', verdict: 'drop', verified: false },
|
|
123
|
-
spamtrap: { status: 'spamtrap', verdict: 'drop', verified: false },
|
|
124
|
-
abuse: { status: 'abuse', verdict: 'drop', verified: false },
|
|
125
|
-
disposable: { status: 'disposable', verdict: 'drop', verified: false },
|
|
126
|
-
};
|
|
92
|
+
// There is intentionally NO shared default status map. Each provider must
|
|
93
|
+
// declare its own `statusMap` / `rules` for the raw status strings it emits
|
|
94
|
+
// (see the provider registries under src/lib/integrations/**). A coarse global
|
|
95
|
+
// map silently coerced provider-specific strings ("deliverable", "accept_all",
|
|
96
|
+
// etc.) into canonical verdicts and let validators depend on guesses instead of
|
|
97
|
+
// documented behavior. With it gone, an unmapped raw status falls through to
|
|
98
|
+
// the typed signal inference below (and ultimately to `unknown`/`hold`) rather
|
|
99
|
+
// than being normalized by a global lookup — loud and provider-declared over
|
|
100
|
+
// silent and implicit.
|
|
127
101
|
|
|
128
102
|
function normalizeKey(value: unknown): string | null {
|
|
129
103
|
if (value == null) return null;
|
|
@@ -186,7 +160,7 @@ function entryForStatus(
|
|
|
186
160
|
map: Record<string, EmailStatusMapEntry> | undefined,
|
|
187
161
|
): EmailStatusMapEntry | null {
|
|
188
162
|
if (!key) return null;
|
|
189
|
-
return map?.[key] ??
|
|
163
|
+
return map?.[key] ?? null;
|
|
190
164
|
}
|
|
191
165
|
|
|
192
166
|
function read(values: Record<string, unknown>, name: string): unknown {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { EmailStatus
|
|
1
|
+
import type { EmailStatus } from './email-status';
|
|
2
2
|
|
|
3
3
|
export const JOB_CHANGE_STATUS_VALUES = [
|
|
4
4
|
'moved',
|
|
@@ -65,7 +65,7 @@ export const DEEPLINE_EXTRACTOR_TARGETS = Object.keys(
|
|
|
65
65
|
DEEPLINE_EXTRACTOR_TARGET_DEFINITIONS,
|
|
66
66
|
) as DeeplineExtractorTarget[];
|
|
67
67
|
|
|
68
|
-
export type DeeplineEmailStatusGetterValue = EmailStatus
|
|
68
|
+
export type DeeplineEmailStatusGetterValue = EmailStatus;
|
|
69
69
|
|
|
70
70
|
export type DeeplineGetterValueMap = {
|
|
71
71
|
id: string;
|
|
@@ -91,7 +91,7 @@ export type DeeplineGetterValueMap = {
|
|
|
91
91
|
status: string;
|
|
92
92
|
job_change: JobChangeGetterValue;
|
|
93
93
|
job_change_status: JobChangeStatus;
|
|
94
|
-
email_status:
|
|
94
|
+
email_status: EmailStatus;
|
|
95
95
|
phone_status: PhoneStatus;
|
|
96
96
|
};
|
|
97
97
|
|
|
@@ -1029,6 +1029,50 @@ export function isToolExecuteResult(
|
|
|
1029
1029
|
);
|
|
1030
1030
|
}
|
|
1031
1031
|
|
|
1032
|
+
function resultRootOf(result: ToolExecuteResult): unknown {
|
|
1033
|
+
return { toolResponse: result.toolOutput };
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
/**
|
|
1037
|
+
* Read a single value out of a tool result — by declared getter name, then by
|
|
1038
|
+
* selector paths, then by key. Used by SDK play helpers (`extractValue`).
|
|
1039
|
+
*/
|
|
1040
|
+
export function readValue(
|
|
1041
|
+
result: ToolExecuteResult,
|
|
1042
|
+
selector: readonly string[] | string,
|
|
1043
|
+
): unknown {
|
|
1044
|
+
if (typeof selector === 'string') {
|
|
1045
|
+
const declared = result.extractedValues[selector]?.get();
|
|
1046
|
+
if (declared != null) return declared;
|
|
1047
|
+
}
|
|
1048
|
+
const root = resultRootOf(result);
|
|
1049
|
+
const paths = Array.isArray(selector) ? selector : [selector];
|
|
1050
|
+
const byPath = findFirstTargetByPath(root, paths);
|
|
1051
|
+
if (byPath) return byPath.value;
|
|
1052
|
+
if (typeof selector === 'string') {
|
|
1053
|
+
const byKey = findFirstTargetByKey(root, selector);
|
|
1054
|
+
if (byKey) return byKey.value;
|
|
1055
|
+
}
|
|
1056
|
+
return null;
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
/**
|
|
1060
|
+
* Read array rows out of a tool result — by selector paths, else the first
|
|
1061
|
+
* declared list accessor. Companion to {@link readValue} for `extractList`.
|
|
1062
|
+
*/
|
|
1063
|
+
export function readList(
|
|
1064
|
+
result: ToolExecuteResult,
|
|
1065
|
+
selector?: readonly string[] | string,
|
|
1066
|
+
): Record<string, unknown>[] {
|
|
1067
|
+
if (selector) {
|
|
1068
|
+
const paths = Array.isArray(selector) ? selector : [selector];
|
|
1069
|
+
const found = findFirstTargetByPath(resultRootOf(result), paths)?.value;
|
|
1070
|
+
const rows = normalizeRows(found);
|
|
1071
|
+
if (rows) return rows;
|
|
1072
|
+
}
|
|
1073
|
+
return Object.values(result.extractedLists)[0]?.get() ?? [];
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1032
1076
|
function metadataInputFromToolExecuteResult(
|
|
1033
1077
|
value: ToolExecuteResult,
|
|
1034
1078
|
): ToolResultMetadataInput {
|
|
@@ -5,8 +5,7 @@ const PRIVATE_KEY_PATTERN =
|
|
|
5
5
|
const BEARER_LITERAL_PATTERN = /\bBearer\s+[A-Za-z0-9._~+/=-]{16,}/i;
|
|
6
6
|
const ASSIGNMENT_SECRET_LITERAL_PATTERN =
|
|
7
7
|
/\b(?:api[_-]?key|token|secret|password)\b\s*[:=]\s*['"][^'"]{12,}['"]/i;
|
|
8
|
-
const HIGH_ENTROPY_LITERAL_PATTERN =
|
|
9
|
-
/['"]([A-Za-z0-9+/=_-]{32,})['"]/g;
|
|
8
|
+
const HIGH_ENTROPY_LITERAL_PATTERN = /['"]([A-Za-z0-9+/=_-]{32,})['"]/g;
|
|
10
9
|
|
|
11
10
|
function shannonEntropy(value: string): number {
|
|
12
11
|
const counts = new Map<string, number>();
|
|
@@ -17,26 +16,38 @@ function shannonEntropy(value: string): number {
|
|
|
17
16
|
}, 0);
|
|
18
17
|
}
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Returns the inline-secret findings in a string (empty if none). The throwing
|
|
21
|
+
* validator below and the workflows→plays migration validator both call this so
|
|
22
|
+
* the heuristics stay a single source of truth (no drift between "publish
|
|
23
|
+
* rejects it" and "transform skips it loudly").
|
|
24
|
+
*/
|
|
25
|
+
export function collectInlineSecretFindings(sourceCode: string): string[] {
|
|
24
26
|
const findings: string[] = [];
|
|
25
|
-
for (const match of
|
|
27
|
+
for (const match of sourceCode.matchAll(SECRET_ENV_PATTERN)) {
|
|
26
28
|
findings.push(`process.env.${match[1]}`);
|
|
27
29
|
}
|
|
28
|
-
if (PRIVATE_KEY_PATTERN.test(
|
|
29
|
-
if (BEARER_LITERAL_PATTERN.test(
|
|
30
|
-
|
|
30
|
+
if (PRIVATE_KEY_PATTERN.test(sourceCode)) findings.push('private key block');
|
|
31
|
+
if (BEARER_LITERAL_PATTERN.test(sourceCode))
|
|
32
|
+
findings.push('bearer token literal');
|
|
33
|
+
if (ASSIGNMENT_SECRET_LITERAL_PATTERN.test(sourceCode)) {
|
|
31
34
|
findings.push('secret-looking assignment literal');
|
|
32
35
|
}
|
|
33
|
-
for (const match of
|
|
36
|
+
for (const match of sourceCode.matchAll(HIGH_ENTROPY_LITERAL_PATTERN)) {
|
|
34
37
|
const literal = match[1] ?? '';
|
|
35
38
|
if (literal.length >= 40 && shannonEntropy(literal) >= 4.2) {
|
|
36
39
|
findings.push('high-entropy string literal');
|
|
37
40
|
break;
|
|
38
41
|
}
|
|
39
42
|
}
|
|
43
|
+
return [...new Set(findings)];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function validatePlaySourceHasNoInlineSecrets(input: {
|
|
47
|
+
sourceCode: string;
|
|
48
|
+
filePath: string;
|
|
49
|
+
}): void {
|
|
50
|
+
const findings = collectInlineSecretFindings(input.sourceCode);
|
|
40
51
|
if (!findings.length) return;
|
|
41
52
|
throw new Error(
|
|
42
53
|
[
|