@deployfoundation/foundation-deploy 0.2.53 → 0.2.55
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/README.md +1 -1
- package/dist/bin/app.js +4 -4
- package/dist/bin/foundation-deploy.js +65 -22
- package/dist/{chunk-nvsj7bbw.js → chunk-345e0grd.js} +1 -1
- package/dist/{chunk-spq5kr1t.js → chunk-68jxyg2n.js} +13 -4
- package/dist/{chunk-mh1206ne.js → chunk-7k4e3d7n.js} +3 -3
- package/dist/{chunk-p7zkm4pp.js → chunk-caqstrxb.js} +1 -1
- package/dist/chunk-gpq7e1kq.js +101 -0
- package/dist/{chunk-m76jt5rc.js → chunk-hvntk8p5.js} +1 -1
- package/dist/chunk-p6yjbtqz.js +13 -0
- package/dist/{chunk-fjdgxw8h.js → chunk-pmjbzsq3.js} +1 -1
- package/dist/{chunk-96fjaysn.js → chunk-rvx0cncy.js} +33 -6
- package/dist/{chunk-jf5rw6h4.js → chunk-vcz9zbb3.js} +36 -11
- package/dist/{chunk-3614v9rr.js → chunk-xt9gdsa0.js} +1 -1
- package/dist/{chunk-1pww63wd.js → chunk-ztrhakgc.js} +90 -15
- package/dist/src/crm-conformance.d.ts +95 -0
- package/dist/src/crm-conformance.js +11 -0
- package/dist/src/index.js +4 -4
- package/package.json +11 -2
- package/src/crm-conformance.ts +27 -0
- package/src/deploy/cli.ts +51 -0
- package/src/deploy/crm.ts +175 -9
- package/src/stacks/api-stack.ts +4 -2
- package/src/stacks/network-stack.ts +15 -0
package/README.md
CHANGED
|
@@ -55,7 +55,7 @@ bun run packages/infra/bin/foundation-deploy.ts <command> --instance <path>
|
|
|
55
55
|
| `slack-manifest` | Print the instance's Slack app manifest as JSON (the Slack CLI's `get-manifest` hook contract). |
|
|
56
56
|
| `stage-customization` | Validate and stage a tenant-owned runtime config. |
|
|
57
57
|
| `crm-key` | Write the company CRM's `crm/1` key, piped in with `--stdin`, into `<secretPrefix>/crm/http` (`crm.provider` kind `http`). |
|
|
58
|
-
| `crm-check` | Run the `crm/1` conformance suite: the read cases against the instance's CRM with its stored key, or, with `--base-url --tenant --key-stdin` and no instance, any server, write cases included. |
|
|
58
|
+
| `crm-check` | Run the `crm/1` conformance suite: the read cases against the instance's CRM with its stored key, or, with `--base-url --tenant --key-stdin` and no instance, any server, write cases included, every case strict given the server's harness endpoint (`--harness-url`) and the deployment's inputs (docs/crm-provider-contract.md#crm-check). A CRM can also import the suite: `@deployfoundation/foundation-deploy/crm-conformance`. |
|
|
59
59
|
| `crm-replay` | Replay the agent's CRM writes (JSONL) into the instance's own table after a rollback to the DynamoDB provider. |
|
|
60
60
|
|
|
61
61
|
`--instance <path>` is required for every command (or `FOUNDATION_INSTANCE_FILE`
|
package/dist/bin/app.js
CHANGED
|
@@ -8,16 +8,16 @@ import {
|
|
|
8
8
|
FoundationPages,
|
|
9
9
|
FoundationPipeline,
|
|
10
10
|
NewsletterStack
|
|
11
|
-
} from "../chunk-
|
|
11
|
+
} from "../chunk-68jxyg2n.js";
|
|
12
12
|
import"../chunk-pxhftba1.js";
|
|
13
|
-
import"../chunk-dz91avrb.js";
|
|
14
13
|
import {
|
|
15
14
|
adminsFor,
|
|
16
15
|
instanceNames,
|
|
17
16
|
loadInstanceFile,
|
|
18
17
|
provisionsIntegration
|
|
19
|
-
} from "../chunk-
|
|
20
|
-
import"../chunk-
|
|
18
|
+
} from "../chunk-pmjbzsq3.js";
|
|
19
|
+
import"../chunk-dz91avrb.js";
|
|
20
|
+
import"../chunk-rvx0cncy.js";
|
|
21
21
|
import"../chunk-5wgwscd0.js";
|
|
22
22
|
|
|
23
23
|
// bin/app.ts
|
|
@@ -1,17 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// @bun
|
|
3
|
-
import {
|
|
4
|
-
RELEASE_BUCKET_ENV,
|
|
5
|
-
RELEASE_VERSION_RE,
|
|
6
|
-
manifestKey,
|
|
7
|
-
releaseCacheDir,
|
|
8
|
-
skillsKey,
|
|
9
|
-
verifyManifest
|
|
10
|
-
} from "../chunk-pxhftba1.js";
|
|
11
|
-
import {
|
|
12
|
-
DEFAULT_RELEASE_BUCKET,
|
|
13
|
-
expectedKeyArn
|
|
14
|
-
} from "../chunk-dz91avrb.js";
|
|
15
3
|
import {
|
|
16
4
|
argv,
|
|
17
5
|
aws,
|
|
@@ -32,16 +20,28 @@ import {
|
|
|
32
20
|
secretExists,
|
|
33
21
|
stackExists,
|
|
34
22
|
stackOutput
|
|
35
|
-
} from "../chunk-
|
|
23
|
+
} from "../chunk-345e0grd.js";
|
|
24
|
+
import {
|
|
25
|
+
RELEASE_BUCKET_ENV,
|
|
26
|
+
RELEASE_VERSION_RE,
|
|
27
|
+
manifestKey,
|
|
28
|
+
releaseCacheDir,
|
|
29
|
+
skillsKey,
|
|
30
|
+
verifyManifest
|
|
31
|
+
} from "../chunk-pxhftba1.js";
|
|
36
32
|
import {
|
|
37
33
|
CUSTOMIZATION_ARTIFACT_NAME,
|
|
38
34
|
instanceNames,
|
|
39
35
|
mongoCollectionPolicyEnv,
|
|
40
36
|
slackCommandPrefix,
|
|
41
37
|
versionedConfigKey
|
|
42
|
-
} from "../chunk-
|
|
43
|
-
import
|
|
44
|
-
|
|
38
|
+
} from "../chunk-pmjbzsq3.js";
|
|
39
|
+
import {
|
|
40
|
+
DEFAULT_RELEASE_BUCKET,
|
|
41
|
+
expectedKeyArn
|
|
42
|
+
} from "../chunk-dz91avrb.js";
|
|
43
|
+
import"../chunk-hvntk8p5.js";
|
|
44
|
+
import"../chunk-xt9gdsa0.js";
|
|
45
45
|
import {
|
|
46
46
|
BASE_SLACK_EVENTS,
|
|
47
47
|
CAPABILITY_IDS,
|
|
@@ -53,7 +53,7 @@ import {
|
|
|
53
53
|
requiredGithubPermissions,
|
|
54
54
|
requiredSlackCommands,
|
|
55
55
|
requiredSlackScopes
|
|
56
|
-
} from "../chunk-
|
|
56
|
+
} from "../chunk-rvx0cncy.js";
|
|
57
57
|
import {
|
|
58
58
|
__require
|
|
59
59
|
} from "../chunk-5wgwscd0.js";
|
|
@@ -2449,6 +2449,20 @@ crm-check options
|
|
|
2449
2449
|
--write with --base-url only: also run the write cases, which
|
|
2450
2450
|
create records and spend the week's outreach allowance;
|
|
2451
2451
|
point it at a sandbox tenant
|
|
2452
|
+
with --base-url --write, what the remaining write cases need:
|
|
2453
|
+
--harness-url <url> the CRM's harness endpoint (crm-harness/1), which does
|
|
2454
|
+
--harness-key-stdin what only a person can; its key comes on stdin
|
|
2455
|
+
--readonly-key-stdin a key for the same tenant without write scopes, on stdin
|
|
2456
|
+
--status-policy <file>
|
|
2457
|
+
the deployment's capabilities.crm.statusPolicy (JSON
|
|
2458
|
+
or YAML)
|
|
2459
|
+
--suppressed-statuses <file>
|
|
2460
|
+
its capabilities.crm.outreach.suppressedStatuses
|
|
2461
|
+
--spend-weekly-allowance
|
|
2462
|
+
drive this week's outreach caps to their limits
|
|
2463
|
+
(a throwaway database only)
|
|
2464
|
+
Keys on stdin go one per line, in this order: the crm/1 key, the read-only
|
|
2465
|
+
key, the harness key (each only when its flag is given).
|
|
2452
2466
|
|
|
2453
2467
|
crm-replay options
|
|
2454
2468
|
--from <file> REQUIRED: the writes to replay, one JSON object per line
|
|
@@ -2462,6 +2476,22 @@ function flag2(args, name) {
|
|
|
2462
2476
|
const index = args.indexOf(name);
|
|
2463
2477
|
return index === -1 ? undefined : args[index + 1];
|
|
2464
2478
|
}
|
|
2479
|
+
function valueFlag(args, name, what) {
|
|
2480
|
+
if (!args.includes(name))
|
|
2481
|
+
return;
|
|
2482
|
+
const value = flag2(args, name);
|
|
2483
|
+
if (value === undefined || value.startsWith("-"))
|
|
2484
|
+
throw new Error(`${name} needs ${what}`);
|
|
2485
|
+
return value;
|
|
2486
|
+
}
|
|
2487
|
+
var CRM_CHECK_TARGET_FLAGS = [
|
|
2488
|
+
"--harness-url",
|
|
2489
|
+
"--harness-key-stdin",
|
|
2490
|
+
"--readonly-key-stdin",
|
|
2491
|
+
"--status-policy",
|
|
2492
|
+
"--suppressed-statuses",
|
|
2493
|
+
"--spend-weekly-allowance"
|
|
2494
|
+
];
|
|
2465
2495
|
function contextFor(args) {
|
|
2466
2496
|
const paths = loadInstanceContext(resolveInstanceFilePath(args, process.env));
|
|
2467
2497
|
return awsContext({
|
|
@@ -2540,12 +2570,22 @@ ${USAGE}`);
|
|
|
2540
2570
|
const tenantId = flag2(args, "--tenant");
|
|
2541
2571
|
if (baseUrl === undefined || tenantId === undefined || !args.includes("--key-stdin"))
|
|
2542
2572
|
throw new Error("crm-check --base-url needs --tenant <id> and --key-stdin");
|
|
2543
|
-
const
|
|
2573
|
+
const harnessUrl = valueFlag(args, "--harness-url", "the CRM's harness endpoint URL");
|
|
2574
|
+
if (harnessUrl !== undefined !== args.includes("--harness-key-stdin"))
|
|
2575
|
+
throw new Error("crm-check --harness-url <url> and --harness-key-stdin go together: the harness key comes on stdin");
|
|
2576
|
+
const statusPolicyFile = valueFlag(args, "--status-policy", "a file");
|
|
2577
|
+
const suppressedStatusesFile = valueFlag(args, "--suppressed-statuses", "a file");
|
|
2578
|
+
const { crmCheckTarget, defaultCrmCommandDeps } = await import("../chunk-ztrhakgc.js");
|
|
2544
2579
|
return crmCheckTarget({
|
|
2545
2580
|
baseUrl,
|
|
2546
2581
|
tenantId,
|
|
2547
2582
|
write: args.includes("--write"),
|
|
2548
|
-
allowSkips: args.includes("--allow-skips")
|
|
2583
|
+
allowSkips: args.includes("--allow-skips"),
|
|
2584
|
+
readOnlyKeyStdin: args.includes("--readonly-key-stdin"),
|
|
2585
|
+
spendWeeklyAllowance: args.includes("--spend-weekly-allowance"),
|
|
2586
|
+
...harnessUrl === undefined ? {} : { harnessUrl },
|
|
2587
|
+
...statusPolicyFile === undefined ? {} : { statusPolicyFile },
|
|
2588
|
+
...suppressedStatusesFile === undefined ? {} : { suppressedStatusesFile }
|
|
2549
2589
|
}, defaultCrmCommandDeps());
|
|
2550
2590
|
}
|
|
2551
2591
|
if (command === "github-app-create") {
|
|
@@ -2613,21 +2653,24 @@ ${USAGE}`);
|
|
|
2613
2653
|
return 0;
|
|
2614
2654
|
}
|
|
2615
2655
|
case "crm-key": {
|
|
2616
|
-
const { crmKey, defaultCrmCommandDeps } = await import("../chunk-
|
|
2656
|
+
const { crmKey, defaultCrmCommandDeps } = await import("../chunk-ztrhakgc.js");
|
|
2617
2657
|
await crmKey(ctx, { stdin: args.includes("--stdin") }, defaultCrmCommandDeps());
|
|
2618
2658
|
return 0;
|
|
2619
2659
|
}
|
|
2620
2660
|
case "crm-check": {
|
|
2621
2661
|
if (args.includes("--write"))
|
|
2622
2662
|
throw new Error("crm-check --write creates records and spends the week's outreach allowance, so it never runs against the instance's own tenant: use --base-url with a sandbox tenant");
|
|
2623
|
-
const
|
|
2663
|
+
const targetOnly = CRM_CHECK_TARGET_FLAGS.filter((name) => args.includes(name));
|
|
2664
|
+
if (targetOnly.length > 0)
|
|
2665
|
+
throw new Error(`crm-check --instance runs the read cases with the deployment's own key and policy; ${targetOnly.join(", ")} feed the write cases: use --base-url --write with a sandbox tenant`);
|
|
2666
|
+
const { crmCheck, defaultCrmCommandDeps } = await import("../chunk-ztrhakgc.js");
|
|
2624
2667
|
return crmCheck(ctx, { allowSkips: args.includes("--allow-skips") }, defaultCrmCommandDeps());
|
|
2625
2668
|
}
|
|
2626
2669
|
case "crm-replay": {
|
|
2627
2670
|
const from = flag2(args, "--from");
|
|
2628
2671
|
if (from === undefined || from.startsWith("-"))
|
|
2629
2672
|
throw new Error("crm-replay needs --from <file>: the writes to replay, as JSONL");
|
|
2630
|
-
const { crmReplay, defaultCrmCommandDeps } = await import("../chunk-
|
|
2673
|
+
const { crmReplay, defaultCrmCommandDeps } = await import("../chunk-ztrhakgc.js");
|
|
2631
2674
|
const result = await crmReplay(ctx, { from }, defaultCrmCommandDeps());
|
|
2632
2675
|
return result.refused.length > 0 ? 1 : 0;
|
|
2633
2676
|
}
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
provisionsIntegration,
|
|
18
18
|
requiresAuthenticatedQueue,
|
|
19
19
|
slackCommandPrefixes
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-pmjbzsq3.js";
|
|
21
21
|
import {
|
|
22
22
|
capabilityScheduledJobs,
|
|
23
23
|
crmOutreachConfigured,
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
parseSchedule,
|
|
29
29
|
scheduledJobBindings,
|
|
30
30
|
webhookJobBindings
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-rvx0cncy.js";
|
|
32
32
|
|
|
33
33
|
// src/stacks/agent-stack.ts
|
|
34
34
|
import * as cdk from "aws-cdk-lib";
|
|
@@ -1942,8 +1942,10 @@ function addConfigLoadAlarm(stack, args) {
|
|
|
1942
1942
|
`${args.instance.displayName}'s runtime is not serving the config deployed for it: the`,
|
|
1943
1943
|
"config failed to load, so it runs on its last good config with company-data",
|
|
1944
1944
|
"capabilities off, or on the built-in fallback. The config_fallback_active or",
|
|
1945
|
-
"config_reload_failed line in the runtime's live log group
|
|
1946
|
-
"
|
|
1945
|
+
"config_reload_failed line in the runtime's live log group says why: its error_message",
|
|
1946
|
+
"names the refused field (record keys shown as *), the YAML error and line, or the",
|
|
1947
|
+
"S3/KMS error and the IAM action it cites (error_type s3_read_failed).",
|
|
1948
|
+
"Fix config.yaml or the grant, run foundation-deploy check, then deploy or config:sync."
|
|
1947
1949
|
].join(" "),
|
|
1948
1950
|
metric: filter.metric({ statistic: "Sum", period: cdk2.Duration.hours(1) }),
|
|
1949
1951
|
threshold: 1,
|
|
@@ -2736,6 +2738,13 @@ class FoundationNetwork extends cdk5.Stack {
|
|
|
2736
2738
|
actions: ["s3:GetObject"],
|
|
2737
2739
|
resources: [`arn:${this.partition}:s3:::prod-${this.region}-starport-layer-bucket/*`]
|
|
2738
2740
|
}));
|
|
2741
|
+
s3Endpoint.addToPolicy(new iam6.PolicyStatement({
|
|
2742
|
+
sid: "AmplifyArtifactsBucket",
|
|
2743
|
+
effect: iam6.Effect.ALLOW,
|
|
2744
|
+
principals: [new iam6.AnyPrincipal],
|
|
2745
|
+
actions: ["s3:GetObject"],
|
|
2746
|
+
resources: [`arn:${this.partition}:s3:::aws-amplify-prod-${this.region}-artifacts/*`]
|
|
2747
|
+
}));
|
|
2739
2748
|
}
|
|
2740
2749
|
if (!skipEndpoints.has("dynamodb")) {
|
|
2741
2750
|
const dynamoEndpoint = this.vpc.addGatewayEndpoint("DynamoDbEndpoint", {
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
classifyOutreach,
|
|
16
16
|
movesToDraftedStatus,
|
|
17
17
|
outreachWeekStart
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-caqstrxb.js";
|
|
19
19
|
import {
|
|
20
20
|
CRM_MAX_ACTIVITY_PAGE_SIZE,
|
|
21
21
|
CRM_MAX_CURSOR_CHARS,
|
|
@@ -29,8 +29,8 @@ import {
|
|
|
29
29
|
parseCrmOutreachQueueResponse,
|
|
30
30
|
parseCrmSearchResponse,
|
|
31
31
|
parseCrmTimestamp
|
|
32
|
-
} from "./chunk-
|
|
33
|
-
import"./chunk-
|
|
32
|
+
} from "./chunk-xt9gdsa0.js";
|
|
33
|
+
import"./chunk-rvx0cncy.js";
|
|
34
34
|
import"./chunk-5wgwscd0.js";
|
|
35
35
|
|
|
36
36
|
// ../capability-crm/src/proxy/repository.ts
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CrmConformanceHarnessError
|
|
3
|
+
} from "./chunk-vcz9zbb3.js";
|
|
4
|
+
import {
|
|
5
|
+
CRM_HTTP_HEADERS,
|
|
6
|
+
crmHttpBaseUrl,
|
|
7
|
+
isCrmHttpKey,
|
|
8
|
+
readCrmHttpBody
|
|
9
|
+
} from "./chunk-hvntk8p5.js";
|
|
10
|
+
import"./chunk-caqstrxb.js";
|
|
11
|
+
import"./chunk-xt9gdsa0.js";
|
|
12
|
+
import"./chunk-rvx0cncy.js";
|
|
13
|
+
import"./chunk-5wgwscd0.js";
|
|
14
|
+
|
|
15
|
+
// ../capability-crm/src/contract/http-harness.ts
|
|
16
|
+
var CRM_HARNESS_CONTRACT = "crm-harness/1";
|
|
17
|
+
var USER_AGENT = "foundation-crm-conformance/1";
|
|
18
|
+
var TIMEOUT_MS = 1e4;
|
|
19
|
+
var MAX_REPLY_BYTES = 64 * 1024;
|
|
20
|
+
function crmHarnessUrl(value) {
|
|
21
|
+
try {
|
|
22
|
+
return crmHttpBaseUrl(value);
|
|
23
|
+
} catch {
|
|
24
|
+
throw new Error("the harness URL must be an HTTPS URL with no userinfo, query or fragment");
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function createCrmHttpHarness(options) {
|
|
28
|
+
const url = crmHarnessUrl(options.url);
|
|
29
|
+
if (!isCrmHttpKey(options.apiKey)) {
|
|
30
|
+
throw new Error("the harness key must be 16 to 1024 visible ASCII characters");
|
|
31
|
+
}
|
|
32
|
+
const { apiKey, tenantId } = options;
|
|
33
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
34
|
+
async function call(request) {
|
|
35
|
+
const signal = AbortSignal.timeout(TIMEOUT_MS);
|
|
36
|
+
let response;
|
|
37
|
+
try {
|
|
38
|
+
response = await fetchImpl(url, {
|
|
39
|
+
method: "POST",
|
|
40
|
+
redirect: "error",
|
|
41
|
+
headers: {
|
|
42
|
+
authorization: `Bearer ${apiKey}`,
|
|
43
|
+
accept: "application/json",
|
|
44
|
+
"content-type": "application/json",
|
|
45
|
+
"user-agent": USER_AGENT,
|
|
46
|
+
[CRM_HTTP_HEADERS.contract]: CRM_HARNESS_CONTRACT
|
|
47
|
+
},
|
|
48
|
+
body: JSON.stringify(request),
|
|
49
|
+
signal
|
|
50
|
+
});
|
|
51
|
+
} catch {
|
|
52
|
+
throw new CrmConformanceHarnessError(`${request.action} ${signal.aborted ? "did not answer in time" : "could not reach the harness"}`);
|
|
53
|
+
}
|
|
54
|
+
let text;
|
|
55
|
+
try {
|
|
56
|
+
text = await readCrmHttpBody(response, MAX_REPLY_BYTES, signal);
|
|
57
|
+
} catch {
|
|
58
|
+
throw new CrmConformanceHarnessError(`${request.action} reply is too large or unreadable`);
|
|
59
|
+
}
|
|
60
|
+
if (response.status < 200 || response.status > 299) {
|
|
61
|
+
throw new CrmConformanceHarnessError(`${request.action} answered HTTP ${response.status}`);
|
|
62
|
+
}
|
|
63
|
+
return text;
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
async setStatusAsPerson(recordType, recordId, status) {
|
|
67
|
+
await call({ action: "set_status_as_person", tenantId, recordType, recordId, status });
|
|
68
|
+
},
|
|
69
|
+
async setDoNotContact(recordType, recordId) {
|
|
70
|
+
await call({ action: "set_do_not_contact", tenantId, recordType, recordId });
|
|
71
|
+
},
|
|
72
|
+
async suppressEmail(email) {
|
|
73
|
+
await call({ action: "suppress_email", tenantId, email });
|
|
74
|
+
},
|
|
75
|
+
async addPersonNote(recordType, recordId, summary) {
|
|
76
|
+
const text = await call({
|
|
77
|
+
action: "add_person_note",
|
|
78
|
+
tenantId,
|
|
79
|
+
recordType,
|
|
80
|
+
recordId,
|
|
81
|
+
summary
|
|
82
|
+
});
|
|
83
|
+
let activityId;
|
|
84
|
+
try {
|
|
85
|
+
const reply = JSON.parse(text);
|
|
86
|
+
if (typeof reply === "object" && reply !== null && "activityId" in reply) {
|
|
87
|
+
activityId = reply.activityId;
|
|
88
|
+
}
|
|
89
|
+
} catch {}
|
|
90
|
+
if (typeof activityId !== "string" || !/^[\x21-\x7e]{1,256}$/.test(activityId)) {
|
|
91
|
+
throw new CrmConformanceHarnessError("add_person_note answered no activityId");
|
|
92
|
+
}
|
|
93
|
+
return activityId;
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
export {
|
|
98
|
+
CRM_HARNESS_CONTRACT,
|
|
99
|
+
createCrmHttpHarness,
|
|
100
|
+
crmHarnessUrl
|
|
101
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CrmConformanceHarnessError,
|
|
3
|
+
runCrmConformance
|
|
4
|
+
} from "./chunk-vcz9zbb3.js";
|
|
5
|
+
import"./chunk-hvntk8p5.js";
|
|
6
|
+
import"./chunk-caqstrxb.js";
|
|
7
|
+
import"./chunk-xt9gdsa0.js";
|
|
8
|
+
import"./chunk-rvx0cncy.js";
|
|
9
|
+
import"./chunk-5wgwscd0.js";
|
|
10
|
+
export {
|
|
11
|
+
CrmConformanceHarnessError,
|
|
12
|
+
runCrmConformance
|
|
13
|
+
};
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
// ../core/src/config.ts
|
|
2
|
-
import { createHash as createHash2 } from "node:crypto";
|
|
3
|
-
import { parse as parseYaml } from "yaml";
|
|
4
|
-
import { z as z4 } from "zod";
|
|
5
|
-
|
|
6
1
|
// ../core/src/capabilities.ts
|
|
7
2
|
import { z } from "zod";
|
|
8
3
|
|
|
@@ -936,6 +931,11 @@ function requiredSlackCommandSuffixes(enabled) {
|
|
|
936
931
|
return [...suffixes].sort();
|
|
937
932
|
}
|
|
938
933
|
|
|
934
|
+
// ../core/src/config.ts
|
|
935
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
936
|
+
import { parse as parseYaml } from "yaml";
|
|
937
|
+
import { z as z4 } from "zod";
|
|
938
|
+
|
|
939
939
|
// ../core/src/jobs.ts
|
|
940
940
|
import { createHash, createHmac, timingSafeEqual } from "node:crypto";
|
|
941
941
|
import { z as z3 } from "zod";
|
|
@@ -1234,6 +1234,33 @@ function parseInstanceConfig(yamlText) {
|
|
|
1234
1234
|
throw new Error(`job ${job.id} owner ${job.owner} must be an instance admin`);
|
|
1235
1235
|
return cfg;
|
|
1236
1236
|
}
|
|
1237
|
+
var ZOD_ISSUE_CODES = new Set(Object.values(z4.ZodIssueCode));
|
|
1238
|
+
var ZOD_PARSED_TYPES = new Set(Object.values(z4.ZodParsedType));
|
|
1239
|
+
var YAML_ERROR_CODES = new Set([
|
|
1240
|
+
"ALIAS_PROPS",
|
|
1241
|
+
"BAD_ALIAS",
|
|
1242
|
+
"BAD_COLLECTION_TYPE",
|
|
1243
|
+
"BAD_DIRECTIVE",
|
|
1244
|
+
"BAD_DQ_ESCAPE",
|
|
1245
|
+
"BAD_INDENT",
|
|
1246
|
+
"BAD_PROP_ORDER",
|
|
1247
|
+
"BAD_SCALAR_START",
|
|
1248
|
+
"BLOCK_AS_IMPLICIT_KEY",
|
|
1249
|
+
"BLOCK_IN_FLOW",
|
|
1250
|
+
"DUPLICATE_KEY",
|
|
1251
|
+
"IMPOSSIBLE",
|
|
1252
|
+
"KEY_OVER_1024_CHARS",
|
|
1253
|
+
"MISSING_CHAR",
|
|
1254
|
+
"MULTILINE_IMPLICIT_KEY",
|
|
1255
|
+
"MULTIPLE_ANCHORS",
|
|
1256
|
+
"MULTIPLE_DOCS",
|
|
1257
|
+
"MULTIPLE_TAGS",
|
|
1258
|
+
"NON_STRING_KEY",
|
|
1259
|
+
"RESOURCE_EXHAUSTION",
|
|
1260
|
+
"TAB_AS_INDENT",
|
|
1261
|
+
"TAG_RESOLVE_FAILED",
|
|
1262
|
+
"UNEXPECTED_TOKEN"
|
|
1263
|
+
]);
|
|
1237
1264
|
|
|
1238
1265
|
// ../core/src/logger.ts
|
|
1239
1266
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
@@ -1295,4 +1322,4 @@ function wrapUntrusted(source, content) {
|
|
|
1295
1322
|
${safe}
|
|
1296
1323
|
</${UNTRUSTED_TAG}>`;
|
|
1297
1324
|
}
|
|
1298
|
-
export { parseSchedule, BASE_SLACK_EVENTS, SLACK_COMMAND_PREFIX_RE, CAPABILITY_IDS, INTEGRATION_CAPABILITY_IDS, requiredGithubPermissions, requiredSlackScopes, requiredSlackCommands, isScheduledJob, isWebhookJob, webhookJobBindings, scheduledJobBindings, jobWebhookKey, capabilityScheduledJobs, crmOutreachConfigured, crmPolicyFingerprint, parseInstanceConfig, PROACTIVE_CHANNEL_MODES, encodeProactiveChannels, wrapUntrusted };
|
|
1325
|
+
export { parseSchedule, RawCrmStatusPolicy, RawCrmStatusesByType, BASE_SLACK_EVENTS, SLACK_COMMAND_PREFIX_RE, CAPABILITY_IDS, INTEGRATION_CAPABILITY_IDS, requiredGithubPermissions, requiredSlackScopes, requiredSlackCommands, isScheduledJob, isWebhookJob, webhookJobBindings, scheduledJobBindings, jobWebhookKey, capabilityScheduledJobs, crmOutreachConfigured, crmPolicyFingerprint, parseInstanceConfig, PROACTIVE_CHANNEL_MODES, encodeProactiveChannels, wrapUntrusted };
|
|
@@ -6,21 +6,26 @@ import {
|
|
|
6
6
|
createCrmHttpClient,
|
|
7
7
|
encodeCrmActor,
|
|
8
8
|
readCrmHttpBody
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-hvntk8p5.js";
|
|
10
10
|
import {
|
|
11
11
|
outreachWeekStart,
|
|
12
12
|
personOwnedStatuses
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-caqstrxb.js";
|
|
14
14
|
import {
|
|
15
15
|
CRM_NOTE_ACTIVITY,
|
|
16
16
|
CRM_OUTREACH_DRAFTED_ACTIVITY,
|
|
17
17
|
CRM_REQUIRED_PROVIDER_OPERATIONS
|
|
18
|
-
} from "./chunk-
|
|
19
|
-
import"./chunk-96fjaysn.js";
|
|
20
|
-
import"./chunk-5wgwscd0.js";
|
|
18
|
+
} from "./chunk-xt9gdsa0.js";
|
|
21
19
|
|
|
22
20
|
// ../capability-crm/src/contract/conformance.ts
|
|
23
21
|
import { randomUUID } from "node:crypto";
|
|
22
|
+
class CrmConformanceHarnessError extends Error {
|
|
23
|
+
constructor(message) {
|
|
24
|
+
super(message);
|
|
25
|
+
this.name = "CrmConformanceHarnessError";
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
24
29
|
class Skip extends Error {
|
|
25
30
|
reason;
|
|
26
31
|
needs;
|
|
@@ -386,9 +391,11 @@ class Suite {
|
|
|
386
391
|
return;
|
|
387
392
|
}
|
|
388
393
|
async transitionCase() {
|
|
389
|
-
const
|
|
390
|
-
|
|
391
|
-
|
|
394
|
+
const given = this.target.disallowedTransition;
|
|
395
|
+
const transition = given ?? this.policyDisallowedTransition();
|
|
396
|
+
if (transition === undefined) {
|
|
397
|
+
throw new Skip(this.target.statusPolicy === undefined ? "no disallowedTransition or statusPolicy given" : "the statusPolicy allows every move from a new record's status");
|
|
398
|
+
}
|
|
392
399
|
const fixture = await this.fixture("transition");
|
|
393
400
|
const recordId = transition.recordType === "company" ? fixture.companyId : fixture.contactId;
|
|
394
401
|
const current = await this.profile(transition.recordType, recordId);
|
|
@@ -398,6 +405,23 @@ class Suite {
|
|
|
398
405
|
status: transition.to,
|
|
399
406
|
idempotencyKey: this.key()
|
|
400
407
|
}, { status: current.status, statusVersion: current.statusVersion ?? 0 }), "refused", "transition_not_allowed");
|
|
408
|
+
return given === undefined ? `${transition.recordType} → ${transition.to}, from the statusPolicy` : undefined;
|
|
409
|
+
}
|
|
410
|
+
policyDisallowedTransition() {
|
|
411
|
+
const policy = this.target.statusPolicy;
|
|
412
|
+
if (policy === undefined)
|
|
413
|
+
return;
|
|
414
|
+
const meta = this.requireMeta();
|
|
415
|
+
for (const recordType of ["company", "contact"]) {
|
|
416
|
+
const rule = policy[recordType];
|
|
417
|
+
const [first, ...others] = meta.statuses[recordType];
|
|
418
|
+
if (rule === undefined || first === undefined)
|
|
419
|
+
continue;
|
|
420
|
+
const exits = Object.hasOwn(rule.agentTransitions, first) ? rule.agentTransitions[first] ?? [] : [];
|
|
421
|
+
const to = others.find((status) => !exits.includes(status));
|
|
422
|
+
if (to !== undefined)
|
|
423
|
+
return { recordType, to };
|
|
424
|
+
}
|
|
401
425
|
return;
|
|
402
426
|
}
|
|
403
427
|
async statusLockedCase() {
|
|
@@ -1102,6 +1126,8 @@ async function expectError(call, code, reason) {
|
|
|
1102
1126
|
function describe(error) {
|
|
1103
1127
|
if (error instanceof Failure)
|
|
1104
1128
|
return error.message;
|
|
1129
|
+
if (error instanceof CrmConformanceHarnessError)
|
|
1130
|
+
return `harness: ${error.message}`;
|
|
1105
1131
|
if (error instanceof CrmHttpError) {
|
|
1106
1132
|
return [
|
|
1107
1133
|
error.code,
|
|
@@ -1112,6 +1138,5 @@ function describe(error) {
|
|
|
1112
1138
|
}
|
|
1113
1139
|
return `unexpected ${error instanceof Error ? error.name : typeof error}`;
|
|
1114
1140
|
}
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
};
|
|
1141
|
+
|
|
1142
|
+
export { CrmConformanceHarnessError, runCrmConformance };
|