@deployfoundation/foundation-deploy 0.2.1 → 0.2.3

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.
@@ -214,6 +214,7 @@ COPY packages/capability-browser/ ./packages/capability-browser/
214
214
  COPY packages/capability-email/ ./packages/capability-email/
215
215
  COPY packages/capability-crm/ ./packages/capability-crm/
216
216
  COPY packages/capability-upwork/ ./packages/capability-upwork/
217
+ COPY packages/capability-etrade/ ./packages/capability-etrade/
217
218
  COPY packages/agent/ ./packages/agent/
218
219
  # Manifest only: the gateway and every proxy are Lambdas, the newsletter is a
219
220
  # Lambda service, and the infra package is the CDK app a pipeline runs. None of
package/dist/bin/app.js CHANGED
@@ -7,12 +7,12 @@ import {
7
7
  FoundationNetwork,
8
8
  FoundationPipeline,
9
9
  NewsletterStack
10
- } from "../chunk-paf33z2h.js";
10
+ } from "../chunk-my0awr1g.js";
11
11
  import {
12
12
  adminsFor,
13
13
  instanceNames,
14
14
  loadInstanceFile
15
- } from "../chunk-gcg67ck3.js";
15
+ } from "../chunk-kf1yvx1w.js";
16
16
 
17
17
  // bin/app.ts
18
18
  import { execFileSync } from "node:child_process";
@@ -48,6 +48,7 @@ var agent = new FoundationAgent(app, names.agent, {
48
48
  itemsTable: data.itemsTable,
49
49
  ...data.crmTable === undefined ? {} : { crmTable: data.crmTable },
50
50
  ...data.upworkApprovalTable === undefined ? {} : { upworkApprovalTable: data.upworkApprovalTable },
51
+ ...data.etradePendingTable === undefined ? {} : { etradePendingTable: data.etradePendingTable },
51
52
  secrets: data.secrets
52
53
  });
53
54
  new FoundationCi(app, names.ci, { env, instance, bucket: data.bucket, dataKey: data.dataKey });
@@ -69,6 +70,7 @@ var api = new FoundationApi(app, names.api, {
69
70
  itemsTable: data.itemsTable,
70
71
  ...data.crmTable === undefined ? {} : { crmTable: data.crmTable },
71
72
  ...data.upworkApprovalTable === undefined ? {} : { upworkApprovalTable: data.upworkApprovalTable },
73
+ ...data.etradePendingTable === undefined ? {} : { etradePendingTable: data.etradePendingTable },
72
74
  secrets: {
73
75
  signing: data.secrets.signing,
74
76
  slackApp: data.secrets.slackApp,
@@ -79,7 +81,9 @@ var api = new FoundationApi(app, names.api, {
79
81
  ...data.secrets.otterApi === undefined ? {} : { otterApi: data.secrets.otterApi },
80
82
  ...data.secrets.knockOauthClient === undefined ? {} : { knockOauthClient: data.secrets.knockOauthClient },
81
83
  ...data.secrets.knockCredential === undefined ? {} : { knockCredential: data.secrets.knockCredential },
82
- ...data.secrets.upwork === undefined ? {} : { upwork: data.secrets.upwork }
84
+ ...data.secrets.upwork === undefined ? {} : { upwork: data.secrets.upwork },
85
+ ...data.secrets.etradeClient === undefined ? {} : { etradeClient: data.secrets.etradeClient },
86
+ ...data.secrets.etradeCredential === undefined ? {} : { etradeCredential: data.secrets.etradeCredential }
83
87
  },
84
88
  agentRuntimeArn: agent.agentRuntimeArn,
85
89
  admins,
@@ -29,7 +29,7 @@ import {
29
29
  stackExists,
30
30
  stackOutput,
31
31
  toolVersion
32
- } from "../chunk-an8qhf3t.js";
32
+ } from "../chunk-7h6zgsa5.js";
33
33
  import {
34
34
  BASE_SLACK_EVENTS,
35
35
  CAPABILITY_IDS,
@@ -45,7 +45,7 @@ import {
45
45
  requiredSlackScopes,
46
46
  skillsKey,
47
47
  slackCommandPrefix
48
- } from "../chunk-gcg67ck3.js";
48
+ } from "../chunk-kf1yvx1w.js";
49
49
 
50
50
  // src/deploy/config-sync.ts
51
51
  import { existsSync, mkdirSync, mkdtempSync, readdirSync } from "node:fs";
@@ -326,6 +326,9 @@ var UPWORK_GRAPHQL_DOCUMENTS = Object.freeze({
326
326
  }`
327
327
  })
328
328
  });
329
+ // ../connectors/src/etrade.ts
330
+ var ETRADE_RESPONSE_MAX_BYTES = 1024 * 1024;
331
+ var MAX_SECRET_JSON_LENGTH = 16 * 1024;
329
332
  // src/deploy/config.ts
330
333
  import { readFileSync } from "node:fs";
331
334
  import { parse } from "yaml";
@@ -748,6 +751,7 @@ function composeRuntimeSecret(inputs) {
748
751
  ...inputs.otterProxyFunctionArn !== undefined && inputs.otterProxyFunctionArn !== "" ? { FOUNDATION_OTTER_PROXY_FUNCTION_ARN: inputs.otterProxyFunctionArn } : {},
749
752
  ...inputs.knockProxyFunctionArn !== undefined && inputs.knockProxyFunctionArn !== "" ? { FOUNDATION_KNOCK_PROXY_FUNCTION_ARN: inputs.knockProxyFunctionArn } : {},
750
753
  ...inputs.upworkProxyFunctionArn !== undefined && inputs.upworkProxyFunctionArn !== "" ? { FOUNDATION_UPWORK_PROXY_ARN: inputs.upworkProxyFunctionArn } : {},
754
+ ...inputs.etradeProxyFunctionArn !== undefined && inputs.etradeProxyFunctionArn !== "" ? { FOUNDATION_ETRADE_PROXY_ARN: inputs.etradeProxyFunctionArn } : {},
751
755
  ...inputs.routineIngressFunctionArn !== undefined && inputs.routineIngressFunctionArn !== "" ? { FOUNDATION_ROUTINE_INGRESS_FUNCTION_ARN: inputs.routineIngressFunctionArn } : {},
752
756
  FOUNDATION_MONGODB_SECRET_ID: names.secretMongodbReadonly,
753
757
  FOUNDATION_SLACK_TEAM_ID: slackApp.team_id !== undefined && slackApp.team_id !== "" ? slackApp.team_id : instance.slack.teamId
@@ -1047,6 +1051,7 @@ async function syncRuntimeSecret(ctx) {
1047
1051
  const otterProxyFunctionArn = dry ? `<${api}.OtterProxyFunctionArn>` : await optionalOutput(ctx, api, "OtterProxyFunctionArn");
1048
1052
  const knockProxyFunctionArn = ctx.instance.integrations.knock ? dry ? `<${api}.KnockProxyFunctionArn>` : await optionalOutput(ctx, api, "KnockProxyFunctionArn") : undefined;
1049
1053
  const upworkProxyFunctionArn = ctx.instance.integrations.upwork ? dry ? `<${api}.UpworkProxyFunctionArn>` : await optionalOutput(ctx, api, "UpworkProxyFunctionArn") : undefined;
1054
+ const etradeProxyFunctionArn = ctx.instance.integrations.etrade ? dry ? `<${api}.EtradeProxyFunctionArn>` : await optionalOutput(ctx, api, "EtradeProxyFunctionArn") : undefined;
1050
1055
  const routineIngressFunctionArn = dry ? `<${api}.RoutineIngressFunctionArn>` : await optionalOutput(ctx, api, "RoutineIngressFunctionArn");
1051
1056
  const slackApp = dry ? {
1052
1057
  bot_token: `<${secretSlackApp}.bot_token>`,
@@ -1071,6 +1076,7 @@ async function syncRuntimeSecret(ctx) {
1071
1076
  ...otterProxyFunctionArn === undefined ? {} : { otterProxyFunctionArn },
1072
1077
  ...knockProxyFunctionArn === undefined ? {} : { knockProxyFunctionArn },
1073
1078
  ...upworkProxyFunctionArn === undefined ? {} : { upworkProxyFunctionArn },
1079
+ ...etradeProxyFunctionArn === undefined ? {} : { etradeProxyFunctionArn },
1074
1080
  ...routineIngressFunctionArn === undefined ? {} : { routineIngressFunctionArn }
1075
1081
  });
1076
1082
  await putSecretJson(ctx, secretRuntime, value);
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env bun
2
2
  import {
3
3
  DEFAULT_RELEASE_BUCKET
4
- } from "../chunk-an8qhf3t.js";
5
- import"../chunk-gcg67ck3.js";
4
+ } from "../chunk-7h6zgsa5.js";
5
+ import"../chunk-kf1yvx1w.js";
6
6
 
7
7
  // bin/release-account.ts
8
8
  import * as cdk2 from "aws-cdk-lib";
@@ -6,7 +6,7 @@ import {
6
6
  manifestKey,
7
7
  releaseCacheDir,
8
8
  verifyManifest
9
- } from "./chunk-gcg67ck3.js";
9
+ } from "./chunk-kf1yvx1w.js";
10
10
 
11
11
  // src/deploy/release.ts
12
12
  import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2 } from "node:fs";
@@ -50,6 +50,8 @@ var RawCrmValue = z.string().min(1).max(64).refine(isCleanCrmValue, {
50
50
  message: "CRM values must not have surrounding whitespace or control characters"
51
51
  });
52
52
  var RawSlackChannel = z.string().regex(/^[CG][A-Z0-9]{8,}$/).max(64);
53
+ var RawPrivateSlackChannel = z.string().regex(/^G[A-Z0-9]{8,}$/).max(64);
54
+ var RawSlackUser = z.string().regex(/^[UW][A-Z0-9]{8,}$/).max(64);
53
55
  var RawUpworkCapability = z.object({
54
56
  enabled: z.boolean().default(false),
55
57
  channels: z.array(RawSlackChannel).max(100).default([])
@@ -66,6 +68,40 @@ var RawUpworkCapability = z.object({
66
68
  seen.add(channel);
67
69
  }
68
70
  }).default({});
71
+ var RawEtradeCapability = z.object({
72
+ enabled: z.boolean().default(false),
73
+ channels: z.array(RawPrivateSlackChannel).max(100).default([]),
74
+ users: z.array(RawSlackUser).max(100).default([])
75
+ }).strict().superRefine((value, ctx) => {
76
+ if (value.enabled && value.channels.length === 0)
77
+ ctx.addIssue({
78
+ code: z.ZodIssueCode.custom,
79
+ path: ["channels"],
80
+ message: "an enabled E*TRADE capability needs at least one private channel"
81
+ });
82
+ if (value.enabled && value.users.length === 0)
83
+ ctx.addIssue({
84
+ code: z.ZodIssueCode.custom,
85
+ path: ["users"],
86
+ message: "an enabled E*TRADE capability needs at least one allowed user"
87
+ });
88
+ for (const [field, values] of [
89
+ ["channels", value.channels],
90
+ ["users", value.users]
91
+ ]) {
92
+ const seen = new Set;
93
+ for (const [index, entry] of values.entries()) {
94
+ if (seen.has(entry)) {
95
+ ctx.addIssue({
96
+ code: z.ZodIssueCode.custom,
97
+ path: [field, index],
98
+ message: `duplicate E*TRADE ${field.slice(0, -1)}: ${entry}`
99
+ });
100
+ }
101
+ seen.add(entry);
102
+ }
103
+ }
104
+ }).default({});
69
105
  var RawAmazonSearchCapability = z.object({
70
106
  enabled: z.boolean().default(true),
71
107
  marketplace: z.string().min(1).optional()
@@ -409,6 +445,16 @@ var CAPABILITIES = [
409
445
  secrets: ["upwork/oauth"],
410
446
  resources: ["upworkProxyFunctionName"],
411
447
  slackCommands: ["upwork-connect"]
448
+ },
449
+ {
450
+ id: "etrade",
451
+ summary: "E*TRADE account data through six fixed reads for named admins and deployment jobs in configured private channels.",
452
+ config: RawEtradeCapability,
453
+ integration: true,
454
+ ...NONE,
455
+ secrets: ["etrade/oauth-client", "etrade/credential"],
456
+ resources: ["etradeProxyFunctionName"],
457
+ slackCommands: ["etrade-connect"]
412
458
  }
413
459
  ];
414
460
  var CAPABILITY_IDS = CAPABILITIES.map((capability) => capability.id);
@@ -466,8 +512,8 @@ var RawAws = z2.object({
466
512
  alarmEmail: z2.string().email().optional()
467
513
  }).strict();
468
514
  var RawSlack = z2.object({
469
- teamId: z2.string().min(1),
470
- appId: z2.string().min(1),
515
+ teamId: z2.string().min(1).max(128),
516
+ appId: z2.string().min(1).max(128),
471
517
  commandPrefix: z2.string().regex(SLACK_COMMAND_PREFIX_RE).optional(),
472
518
  legacyCommandPrefixes: z2.array(z2.string().regex(SLACK_COMMAND_PREFIX_RE)).max(3).default([])
473
519
  }).strict();
@@ -512,8 +558,8 @@ var RawProactive = z2.object({
512
558
  maxPerHour: z2.number().int().min(1).max(500).default(20)
513
559
  }).strict().default({ channels: [] });
514
560
  var RawNaming = z2.object({
515
- prefix: z2.string().regex(/^[A-Z][A-Za-z0-9]*$/),
516
- secretPrefix: z2.string().regex(/^[a-z][a-z0-9-]*$/),
561
+ prefix: z2.string().min(1).max(32).regex(/^[A-Z][A-Za-z0-9]*$/),
562
+ secretPrefix: z2.string().min(1).max(40).regex(/^[a-z][a-z0-9-]*$/),
517
563
  runtimeName: z2.string().regex(/^[a-zA-Z][a-zA-Z0-9_]{0,47}$/)
518
564
  }).strict();
519
565
  var INSTANCE_ID_RE = /^[a-z][a-z0-9-]*$/;
@@ -629,6 +675,7 @@ var RawIntegrations = z2.object({
629
675
  browser: z2.boolean().default(false),
630
676
  knock: z2.boolean().default(false),
631
677
  upwork: z2.boolean().default(false),
678
+ etrade: z2.boolean().default(false),
632
679
  crm: z2.boolean().default(false),
633
680
  crmStorage: z2.boolean().optional()
634
681
  }).strict().default({}).transform((integrations) => ({
@@ -637,8 +684,8 @@ var RawIntegrations = z2.object({
637
684
  }));
638
685
  var RawLicense = z2.object({ key: z2.string().min(1) }).strict();
639
686
  var RawInstance = z2.object({
640
- name: z2.string().regex(/^[a-z][a-z0-9-]*$/),
641
- displayName: z2.string().min(1),
687
+ name: z2.string().min(1).max(63).regex(/^[a-z][a-z0-9-]*$/),
688
+ displayName: z2.string().min(1).max(120),
642
689
  pending: z2.boolean().default(false),
643
690
  aws: RawAws,
644
691
  slack: RawSlack,
@@ -730,6 +777,8 @@ function instanceNames(instance) {
730
777
  secretKnockOauthClient: secret("knock/oauth-client"),
731
778
  secretKnockCredential: secret("knock/credential"),
732
779
  secretUpwork: secret("upwork/oauth"),
780
+ secretEtradeClient: secret("etrade/oauth-client"),
781
+ secretEtradeCredential: secret("etrade/credential"),
733
782
  secretRuntime: secret("agent/runtime"),
734
783
  secretLicenseLastVerified: secret("license/last-verified"),
735
784
  newsletterTokenSigningSecret: secret("newsletter/token-signing"),
@@ -751,6 +800,7 @@ function instanceNames(instance) {
751
800
  browserProxyFunctionName: stack("BrowserProxy"),
752
801
  knockProxyFunctionName: stack("KnockProxy"),
753
802
  upworkProxyFunctionName: stack("UpworkProxy"),
803
+ etradeProxyFunctionName: stack("EtradeProxy"),
754
804
  defaultRepo: instance.github.defaultRepo ?? instance.github.repo
755
805
  };
756
806
  }
@@ -774,12 +824,12 @@ import { dirname, isAbsolute, resolve } from "node:path";
774
824
  import { AsyncLocalStorage } from "node:async_hooks";
775
825
  var contextStorage = new AsyncLocalStorage;
776
826
  // ../core/src/config.ts
777
- import { createHash } from "node:crypto";
827
+ import { createHash as createHash2 } from "node:crypto";
778
828
  import { parse as parseYaml2 } from "yaml";
779
829
  import { z as z4 } from "zod";
780
830
 
781
831
  // ../core/src/jobs.ts
782
- import { createHmac, timingSafeEqual } from "node:crypto";
832
+ import { createHash, createHmac, timingSafeEqual } from "node:crypto";
783
833
  import { z as z3 } from "zod";
784
834
  var JOB_ID_PATTERN = /^[a-z0-9][a-z0-9-]{0,40}$/;
785
835
  var RawScheduleTrigger = z3.object({
@@ -817,6 +867,15 @@ function isWebhookJob(job) {
817
867
  function webhookJobBindings(jobs) {
818
868
  return jobs.filter(isWebhookJob).map(({ id, name, channel, prompt }) => ({ id, name, channel, prompt }));
819
869
  }
870
+ function scheduledJobFingerprint(input) {
871
+ return createHash("sha256").update(JSON.stringify([input.routineId, input.name, input.channel, input.prompt])).digest("hex");
872
+ }
873
+ function scheduledJobBindings(jobs) {
874
+ return jobs.filter(isScheduledJob).map(({ id, name, channel, prompt }) => {
875
+ const input = { routineId: `job-${id}`, name, channel, prompt };
876
+ return { routineId: input.routineId, fingerprint: scheduledJobFingerprint(input) };
877
+ });
878
+ }
820
879
  function jobWebhookKey(instanceSecret, jobId) {
821
880
  return createHmac("sha256", instanceSecret).update(`foundation-job:${jobId}`).digest("hex");
822
881
  }
@@ -888,7 +947,7 @@ function crmOutreachConfigured(outreach) {
888
947
  return outreach.eligibleStatuses.length > 0;
889
948
  }
890
949
  function crmPolicyFingerprint(config) {
891
- return createHash("sha256").update(JSON.stringify({
950
+ return createHash2("sha256").update(JSON.stringify({
892
951
  channels: config.channels,
893
952
  statuses: config.statuses,
894
953
  activityTypes: config.activityTypes,
@@ -1194,7 +1253,7 @@ var WEB_SEARCH_CONNECTOR_VERSION = "1.2.0";
1194
1253
  var LIVE_ENDPOINT_NAME = "live";
1195
1254
 
1196
1255
  // src/release/manifest.ts
1197
- import { createHash as createHash2 } from "node:crypto";
1256
+ import { createHash as createHash3 } from "node:crypto";
1198
1257
  import { readFileSync as readFileSync2, statSync } from "node:fs";
1199
1258
  import { z as z5 } from "zod";
1200
1259
  var RELEASE_VERSION_RE = /^v\d+\.\d+\.\d+$/;
@@ -1248,7 +1307,7 @@ function skillsKey(version) {
1248
1307
  return `${releasePrefix(version)}/skills.tar.gz`;
1249
1308
  }
1250
1309
  function sha256Hex(bytes) {
1251
- return createHash2("sha256").update(bytes).digest("hex");
1310
+ return createHash3("sha256").update(bytes).digest("hex");
1252
1311
  }
1253
1312
  function sha256File(path) {
1254
1313
  return sha256Hex(readFileSync2(path));
@@ -1381,6 +1440,7 @@ var LAMBDA_ENTRY_POINTS = {
1381
1440
  "knock-proxy": { entry: "packages/capability-knock/src/proxy/handler.ts" },
1382
1441
  "crm-proxy": { entry: "packages/capability-crm/src/proxy/handler.ts" },
1383
1442
  "upwork-proxy": { entry: "packages/capability-upwork/src/proxy/handler.ts" },
1443
+ "etrade-proxy": { entry: "packages/capability-etrade/src/proxy/handler.ts" },
1384
1444
  "browser-proxy": { entry: "packages/gateway/src/browser-proxy.ts", playwright: true },
1385
1445
  "newsletter-public": { entry: "packages/newsletter/src/lambda-public.ts" },
1386
1446
  "newsletter-campaign": { entry: "packages/newsletter/src/lambda-campaign.ts" },
@@ -1511,4 +1571,4 @@ function ecrRepositoryArn(repositoryUri) {
1511
1571
  return `arn:aws:ecr:${region}:${account}:repository/${name}`;
1512
1572
  }
1513
1573
 
1514
- export { BASE_SLACK_EVENTS, CAPABILITY_IDS, requiredGithubPermissions, requiredSlackScopes, requiredSlackCommands, CUSTOMIZATION_ARTIFACT_NAME, newsletterManifestFor, instanceNames, slackCommandPrefix, slackCommandPrefixes, isScheduledJob, isWebhookJob, webhookJobBindings, jobWebhookKey, crmOutreachConfigured, crmPolicyFingerprint, parseInstanceConfig, parseSchedule, loadInstanceFile, configPathFor, capabilityEnabled, requiresAuthenticatedQueue, enabledIntegrations, provisionsIntegration, adminsFor, WEB_SEARCH_TARGET, WEB_SEARCH_CONNECTOR_VERSION, LIVE_ENDPOINT_NAME, RELEASE_VERSION_RE, parseManifest, manifestKey, lambdaKey, skillsKey, buildManifest, manifestSigningPayload, verifyManifest, lambdaBundleContext, BUN_VERSION, LAMBDA_ENTRY_POINTS, skipBundle, RELEASE_BUCKET_ENV, RELEASE_DIR_ENV, releaseCacheDir, releaseSource, lambdaCode, agentImage, agentImageTagRequired, releaseImageRepositoryArn, ecrRepositoryArn };
1574
+ export { BASE_SLACK_EVENTS, CAPABILITY_IDS, requiredGithubPermissions, requiredSlackScopes, requiredSlackCommands, CUSTOMIZATION_ARTIFACT_NAME, newsletterManifestFor, instanceNames, slackCommandPrefix, slackCommandPrefixes, isScheduledJob, isWebhookJob, webhookJobBindings, scheduledJobBindings, jobWebhookKey, crmOutreachConfigured, crmPolicyFingerprint, parseInstanceConfig, parseSchedule, loadInstanceFile, configPathFor, capabilityEnabled, requiresAuthenticatedQueue, enabledIntegrations, provisionsIntegration, adminsFor, WEB_SEARCH_TARGET, WEB_SEARCH_CONNECTOR_VERSION, LIVE_ENDPOINT_NAME, RELEASE_VERSION_RE, parseManifest, manifestKey, lambdaKey, skillsKey, buildManifest, manifestSigningPayload, verifyManifest, lambdaBundleContext, BUN_VERSION, LAMBDA_ENTRY_POINTS, skipBundle, RELEASE_BUCKET_ENV, RELEASE_DIR_ENV, releaseCacheDir, releaseSource, lambdaCode, agentImage, agentImageTagRequired, releaseImageRepositoryArn, ecrRepositoryArn };