@anthusai/papyrus 0.0.0 → 1.0.0-next.1
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/LICENSE +21 -0
- package/amplify/auth/resource.js +61 -0
- package/amplify/auth/resource.ts +65 -0
- package/amplify/data/resource.js +2037 -0
- package/amplify/data/resource.ts +2148 -0
- package/amplify/data/schema.js +1850 -0
- package/amplify/data/schema.ts +1953 -0
- package/amplify/functions/assignment-action/handler.ts +1416 -0
- package/amplify/functions/assignment-action/resource.js +8 -0
- package/amplify/functions/assignment-action/resource.ts +9 -0
- package/amplify/functions/category-action/handler.ts +2259 -0
- package/amplify/functions/category-action/resource.js +8 -0
- package/amplify/functions/category-action/resource.ts +9 -0
- package/amplify/functions/console-chat-responder/Cargo.lock +3456 -0
- package/amplify/functions/console-chat-responder/Cargo.toml +26 -0
- package/amplify/functions/console-chat-responder/Dockerfile +19 -0
- package/amplify/functions/console-chat-responder/py/execute_tactus_runner.py +116 -0
- package/amplify/functions/console-chat-responder/resource.js +153 -0
- package/amplify/functions/console-chat-responder/resource.ts +179 -0
- package/amplify/functions/console-chat-responder/src/main.rs +3242 -0
- package/amplify/functions/email-submission-processor/handler.py +46 -0
- package/amplify/functions/email-submission-processor/requirements.txt +2 -0
- package/amplify/functions/email-submission-processor/resource.js +56 -0
- package/amplify/functions/email-submission-processor/resource.ts +69 -0
- package/amplify/functions/graphql-jwt-authorizer/handler.ts +193 -0
- package/amplify/functions/graphql-jwt-authorizer/resource.js +14 -0
- package/amplify/functions/graphql-jwt-authorizer/resource.ts +15 -0
- package/amplify/functions/knowledge-query/handler.py +1 -0
- package/amplify/functions/knowledge-query/requirements.txt +3 -0
- package/amplify/functions/knowledge-query/resource.js +50 -0
- package/amplify/functions/knowledge-query/resource.ts +61 -0
- package/amplify/functions/manage-user-role/handler.ts +799 -0
- package/amplify/functions/manage-user-role/resource.js +8 -0
- package/amplify/functions/manage-user-role/resource.ts +9 -0
- package/amplify/functions/model-attachment-upload/handler.ts +387 -0
- package/amplify/functions/model-attachment-upload/resource.js +8 -0
- package/amplify/functions/model-attachment-upload/resource.ts +9 -0
- package/amplify/functions/newsroom-summary/handler.ts +321 -0
- package/amplify/functions/newsroom-summary/resource.js +8 -0
- package/amplify/functions/newsroom-summary/resource.ts +9 -0
- package/amplify/functions/procedure-action/handler.ts +621 -0
- package/amplify/functions/procedure-action/resource.js +8 -0
- package/amplify/functions/procedure-action/resource.ts +9 -0
- package/amplify/functions/reader-settings/handler.ts +322 -0
- package/amplify/functions/reader-settings/resource.js +8 -0
- package/amplify/functions/reader-settings/resource.ts +9 -0
- package/amplify/functions/ses-inbound-receive/handler.ts +493 -0
- package/amplify/functions/ses-inbound-receive/resource.js +8 -0
- package/amplify/functions/ses-inbound-receive/resource.ts +9 -0
- package/amplify/functions/shared/amplify-secrets.ts +65 -0
- package/amplify/functions/shared/console-chat-enqueue.ts +117 -0
- package/amplify/functions/shared/email-mime-intake.ts +196 -0
- package/amplify/functions/shared/email-submission.ts +415 -0
- package/amplify/functions/shared/inbound-email-intake.ts +37 -0
- package/amplify/functions/shared/lambda-data-client.ts +21 -0
- package/amplify/functions/shared/model-payloads.ts +186 -0
- package/amplify/functions/shared/python-bundle.js +115 -0
- package/amplify/functions/shared/python-bundle.ts +141 -0
- package/amplify/functions/shared/slack-events.ts +111 -0
- package/amplify/functions/slack-delivery/handler.py +112 -0
- package/amplify/functions/slack-delivery/requirements.txt +2 -0
- package/amplify/functions/slack-delivery/resource.js +38 -0
- package/amplify/functions/slack-delivery/resource.ts +51 -0
- package/amplify/functions/slack-events/handler.ts +155 -0
- package/amplify/functions/slack-events/resource.js +11 -0
- package/amplify/functions/slack-events/resource.ts +12 -0
- package/amplify/inbound-email/stack.js +46 -0
- package/amplify/inbound-email/stack.ts +61 -0
- package/amplify/package.json +3 -0
- package/amplify/site-backend-config.js +1 -0
- package/amplify/site-backend-config.ts +30 -0
- package/amplify/site-backend.js +587 -0
- package/amplify/site-backend.ts +704 -0
- package/amplify/storage/resource.js +18 -0
- package/amplify/storage/resource.ts +19 -0
- package/app/[year]/[month]/[day]/[articleSlug]/page.tsx +62 -0
- package/app/[year]/[month]/[day]/edition-route-page.tsx +53 -0
- package/app/[year]/[month]/[day]/page/[pageNumber]/page.tsx +32 -0
- package/app/[year]/[month]/[day]/page.tsx +22 -0
- package/app/[year]/[month]/[day]/section/[sectionKey]/page.tsx +32 -0
- package/app/api/archive/editions/route.ts +18 -0
- package/app/api/media/[...path]/route.ts +35 -0
- package/app/api/revalidate/route.ts +94 -0
- package/app/archive/page.tsx +24 -0
- package/app/articles/[slug]/page.tsx +35 -0
- package/app/globals.css +10329 -0
- package/app/layout.tsx +146 -0
- package/app/newsroom/[section]/[...selection]/page.tsx +50 -0
- package/app/newsroom/[section]/page.tsx +47 -0
- package/app/newsroom/forum/[threadId]/page.tsx +35 -0
- package/app/newsroom/insights/[threadId]/page.tsx +22 -0
- package/app/newsroom/insights/page.tsx +14 -0
- package/app/newsroom/layout.tsx +12 -0
- package/app/newsroom/page.tsx +31 -0
- package/app/newsroom/sections/[sectionId]/page.tsx +17 -0
- package/app/page.tsx +272 -0
- package/app/settings/page.tsx +14 -0
- package/app/tailwind.css +65 -0
- package/bin/papyrus-app.mjs +10 -0
- package/bin/papyrus-infra.mjs +39 -0
- package/components/ai-elements/conversation.tsx +168 -0
- package/components/ai-elements/message.tsx +360 -0
- package/components/ai-elements/model-selector.tsx +30 -0
- package/components/ai-elements/prompt-input.tsx +1461 -0
- package/components/ai-elements/shimmer.tsx +76 -0
- package/components/ai-elements/suggestion.tsx +54 -0
- package/components/ai-elements/tool.tsx +173 -0
- package/components/amplify-client-provider.tsx +140 -0
- package/components/archive-grid.tsx +89 -0
- package/components/archive-shell.tsx +41 -0
- package/components/article-video.tsx +47 -0
- package/components/blog-page-background.tsx +20 -0
- package/components/dev-sandbox-editor-sign-in.tsx +60 -0
- package/components/news-desk-client-provider.tsx +211 -0
- package/components/news-desk-page.tsx +119 -0
- package/components/news-desk-taxonomy-client.ts +3684 -0
- package/components/newspaper.tsx +1929 -0
- package/components/newsroom-assignments-view.tsx +664 -0
- package/components/newsroom-ops-overview.tsx +226 -0
- package/components/newsroom-ops-shell.tsx +319 -0
- package/components/newsroom-references-view.tsx +598 -0
- package/components/newsroom-topics-view.tsx +425 -0
- package/components/papyrus-console-shell.tsx +1912 -0
- package/components/pictogram-figure.tsx +65 -0
- package/components/presentation-footer.tsx +146 -0
- package/components/reader-auth-control.tsx +162 -0
- package/components/reader-auth-state.ts +122 -0
- package/components/reader-settings.ts +332 -0
- package/components/reader-theme-control.tsx +85 -0
- package/components/reference-source-preview.tsx +163 -0
- package/components/settings-page.tsx +239 -0
- package/components/topic-steering-workspace.tsx +18063 -0
- package/components/ui/badge.tsx +30 -0
- package/components/ui/button-group.tsx +87 -0
- package/components/ui/button.tsx +58 -0
- package/components/ui/card.tsx +32 -0
- package/components/ui/command.tsx +194 -0
- package/components/ui/dialog.tsx +158 -0
- package/components/ui/dropdown-menu.tsx +266 -0
- package/components/ui/hover-card.tsx +51 -0
- package/components/ui/input-group.tsx +158 -0
- package/components/ui/input.tsx +20 -0
- package/components/ui/scroll-area.tsx +16 -0
- package/components/ui/select.tsx +199 -0
- package/components/ui/separator.tsx +23 -0
- package/components/ui/sheet.tsx +92 -0
- package/components/ui/spinner.tsx +10 -0
- package/components/ui/tabs.tsx +113 -0
- package/components/ui/textarea.tsx +18 -0
- package/components/ui/tooltip.tsx +66 -0
- package/components/use-resolved-papyrus-theme.ts +47 -0
- package/infra/amplify-app-shell.js +109 -0
- package/infra/amplify-app-shell.ts +155 -0
- package/infra/index.js +12 -0
- package/lib/amplify-outputs-guard.ts +24 -0
- package/lib/amplify-outputs-path.ts +16 -0
- package/lib/amplify-server-runtime.ts +63 -0
- package/lib/archive-data.ts +30 -0
- package/lib/archive-types.ts +15 -0
- package/lib/articles.ts +320 -0
- package/lib/cached-content-repository.ts +89 -0
- package/lib/category-dashboard.ts +37 -0
- package/lib/category-repository.ts +1907 -0
- package/lib/console-chat-client.ts +1316 -0
- package/lib/console-web-context.ts +118 -0
- package/lib/content-load-timing.ts +23 -0
- package/lib/content-repository.ts +40 -0
- package/lib/content-types.ts +104 -0
- package/lib/define-site.js +3 -0
- package/lib/define-site.ts +21 -0
- package/lib/demo-amplify-outputs.ts +15 -0
- package/lib/dev-sandbox-editor-auth.ts +18 -0
- package/lib/doctrine.ts +128 -0
- package/lib/edition-routes.ts +155 -0
- package/lib/edition-sections.ts +111 -0
- package/lib/empty-edition-layout-plan.ts +109 -0
- package/lib/excerpts.ts +8 -0
- package/lib/graphql-content-repository.ts +986 -0
- package/lib/image-url.ts +10 -0
- package/lib/layout-plan.ts +981 -0
- package/lib/layout-scenarios.ts +564 -0
- package/lib/markus-body.ts +168 -0
- package/lib/markus-ir.ts +235 -0
- package/lib/markus-projection.ts +243 -0
- package/lib/markus-to-article.ts +93 -0
- package/lib/news-desk-session.ts +160 -0
- package/lib/newspaper-layout.ts +3459 -0
- package/lib/newsroom-assignments.ts +173 -0
- package/lib/newsroom-base-path.ts +48 -0
- package/lib/newsroom-card-layout.ts +102 -0
- package/lib/newsroom-category-drilldown.ts +133 -0
- package/lib/newsroom-demo-dashboard.ts +267 -0
- package/lib/newsroom-demo-profile.ts +46 -0
- package/lib/newsroom-forum-routes.ts +153 -0
- package/lib/newsroom-index-filters.ts +202 -0
- package/lib/newsroom-knowledge-query-request.ts +51 -0
- package/lib/newsroom-list-selection.ts +42 -0
- package/lib/newsroom-nav.ts +42 -0
- package/lib/newsroom-references.ts +116 -0
- package/lib/newsroom-sections.ts +52 -0
- package/lib/newsroom-topics.ts +118 -0
- package/lib/papyrus-object-kinds.ts +24 -0
- package/lib/papyrus-web-locations.ts +480 -0
- package/lib/pilobol-sample.ts +167 -0
- package/lib/presentation-footer.ts +92 -0
- package/lib/pretext-layout.ts +179 -0
- package/lib/public-placeholder-config.ts +102 -0
- package/lib/publication-items.ts +181 -0
- package/lib/reader-route-config.ts +1 -0
- package/lib/reader-static-params.ts +56 -0
- package/lib/reader-storage-url.ts +63 -0
- package/lib/reference-display.ts +74 -0
- package/lib/reference-policy.ts +97 -0
- package/lib/reference-source-preview.ts +175 -0
- package/lib/renderer-config.ts +50 -0
- package/lib/renderer.ts +87 -0
- package/lib/reporting-story-budget.ts +638 -0
- package/lib/semantic-graph.ts +554 -0
- package/lib/site-brand-tenant.ts +41 -0
- package/lib/site-brand.ts +179 -0
- package/lib/site-renderer.ts +17 -0
- package/lib/site-stack.ts +195 -0
- package/lib/themed-image.ts +30 -0
- package/lib/utils.ts +6 -0
- package/middleware.ts +78 -0
- package/package.json +296 -1
- package/publications/anth_us/brand.ts +68 -0
- package/publications/anth_us/theme.css +66 -0
- package/publications/papyrus/theme.css +11 -0
- package/publications/pilobol_us/brand.ts +37 -0
- package/publications/pilobol_us/theme.css +140 -0
- package/publications/threat_intelligence/blog-defense/graph.ts +636 -0
- package/publications/threat_intelligence/blog-defense/layout.ts +934 -0
- package/publications/threat_intelligence/blog-defense/page-background.tsx +590 -0
- package/publications/threat_intelligence/brand.ts +40 -0
- package/publications/threat_intelligence/pictograms/art.tsx +608 -0
- package/publications/threat_intelligence/pictograms/figure.tsx +82 -0
- package/publications/threat_intelligence/pictograms/registry.ts +30 -0
- package/publications/threat_intelligence/pictograms/system.tsx +305 -0
- package/publications/threat_intelligence/seed/seed-edition-content.json +629 -0
- package/publications/threat_intelligence/theme.css +1440 -0
- package/renderers/markus/index.ts +25 -0
- package/renderers/markus/stubs.tsx +33 -0
- package/renderers/pretext/article-page.tsx +174 -0
- package/renderers/pretext/index.ts +30 -0
- package/renderers/pretext/layouts.ts +16 -0
- package/renderers/pretext/presentation-shell.tsx +477 -0
- package/routes.manifest.json +182 -0
- package/scripts/fixtures/pilobol-sample.ast.json +234 -0
- package/src/backend.js +1 -0
- package/src/backend.ts +3 -0
- package/src/sync.mjs +87 -0
- package/src/with-papyrus.d.mts +2 -0
- package/src/with-papyrus.mjs +47 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "papyrus_console_chat_responder"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
edition = "2024"
|
|
5
|
+
|
|
6
|
+
[dependencies]
|
|
7
|
+
anyhow = "1.0.102"
|
|
8
|
+
aws-config = "1.8.17"
|
|
9
|
+
aws-credential-types = "1.2.14"
|
|
10
|
+
aws-sdk-dynamodb = "1.112.0"
|
|
11
|
+
aws-sdk-ssm = "1.110.0"
|
|
12
|
+
aws-sigv4 = { version = "1.4.4", features = ["http1"] }
|
|
13
|
+
aws_lambda_events = "1.2.0"
|
|
14
|
+
base64 = "0.22.1"
|
|
15
|
+
chrono = { version = "0.4.44", features = ["serde"] }
|
|
16
|
+
futures-util = "0.3.31"
|
|
17
|
+
http = "1.4.0"
|
|
18
|
+
lambda_runtime = "1.2.0"
|
|
19
|
+
reqwest = { version = "0.13.3", features = ["json", "stream"] }
|
|
20
|
+
serde = { version = "1.0.228", features = ["derive"] }
|
|
21
|
+
serde_json = "1.0.150"
|
|
22
|
+
sha2 = "0.11.0"
|
|
23
|
+
tokio = { version = "1.52.3", features = ["macros", "rt-multi-thread", "io-util", "process", "time"] }
|
|
24
|
+
tracing = "0.1.44"
|
|
25
|
+
tracing-subscriber = "0.3.23"
|
|
26
|
+
uuid = { version = "1.23.1", features = ["v4"] }
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
FROM rust:1.91.1-bookworm AS build
|
|
2
|
+
WORKDIR /workspace/amplify/functions/console-chat-responder
|
|
3
|
+
|
|
4
|
+
COPY amplify/functions/console-chat-responder/Cargo.toml amplify/functions/console-chat-responder/Cargo.lock ./
|
|
5
|
+
COPY amplify/functions/console-chat-responder/src ./src
|
|
6
|
+
RUN cargo build --release
|
|
7
|
+
|
|
8
|
+
FROM public.ecr.aws/lambda/provided:al2023
|
|
9
|
+
COPY --from=build /workspace/amplify/functions/console-chat-responder/target/release/papyrus_console_chat_responder /var/runtime/bootstrap
|
|
10
|
+
RUN dnf --setopt=cachedir=/tmp/dnf-cache install -y python3 python3-pip \
|
|
11
|
+
&& python3 -m pip install --no-cache-dir PyYAML botocore tiktoken \
|
|
12
|
+
&& python3 -m pip install --no-cache-dir tactus==0.44.1 \
|
|
13
|
+
&& dnf clean all \
|
|
14
|
+
&& rm -rf /tmp/dnf-cache /var/cache/dnf /var/cache/yum
|
|
15
|
+
COPY src /opt/papyrus/src
|
|
16
|
+
COPY amplify/functions/console-chat-responder/py/execute_tactus_runner.py /opt/papyrus/execute_tactus_runner.py
|
|
17
|
+
ENV PYTHONPATH=/opt/papyrus/src
|
|
18
|
+
RUN chmod +x /var/runtime/bootstrap
|
|
19
|
+
CMD ["/var/runtime/bootstrap"]
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
import sys
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
SEARCH_PATHS = [
|
|
11
|
+
Path("/opt/papyrus/src"),
|
|
12
|
+
Path.cwd() / "src",
|
|
13
|
+
]
|
|
14
|
+
for root in SEARCH_PATHS:
|
|
15
|
+
if root.exists() and str(root) not in sys.path:
|
|
16
|
+
sys.path.insert(0, str(root))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _ensure_graphql_authoring_jwt() -> None:
|
|
20
|
+
if not os.environ.get("AWS_LAMBDA_FUNCTION_NAME", "").strip():
|
|
21
|
+
return
|
|
22
|
+
from papyrus_content.env import ensure_graphql_authoring_jwt # type: ignore
|
|
23
|
+
|
|
24
|
+
ensure_graphql_authoring_jwt()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _error(code: str, message: str, *, retryable: bool = False) -> dict[str, Any]:
|
|
28
|
+
return {
|
|
29
|
+
"ok": False,
|
|
30
|
+
"error": {
|
|
31
|
+
"code": code,
|
|
32
|
+
"message": message,
|
|
33
|
+
"retryable": retryable,
|
|
34
|
+
},
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _docs_index() -> dict[str, Any]:
|
|
39
|
+
try:
|
|
40
|
+
from papyrus_newsroom.tactus_runtime import DOCS # type: ignore
|
|
41
|
+
except Exception as exc:
|
|
42
|
+
return _error("runner_unavailable", f"Unable to import papyrus_newsroom.tactus_runtime: {exc}")
|
|
43
|
+
|
|
44
|
+
entries: list[dict[str, str]] = []
|
|
45
|
+
for doc in DOCS.values():
|
|
46
|
+
entries.append(
|
|
47
|
+
{
|
|
48
|
+
"id": str(doc.get("id") or ""),
|
|
49
|
+
"title": str(doc.get("title") or ""),
|
|
50
|
+
"summary": str(doc.get("summary") or ""),
|
|
51
|
+
"namespace": str(doc.get("namespace") or ""),
|
|
52
|
+
}
|
|
53
|
+
)
|
|
54
|
+
entries.sort(key=lambda entry: entry["id"])
|
|
55
|
+
return {"entries": entries}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _coerce_web_ui_context(payload: Any) -> dict[str, Any] | None:
|
|
59
|
+
if not isinstance(payload, dict):
|
|
60
|
+
return None
|
|
61
|
+
web_path = str(payload.get("webPath") or payload.get("web_path") or "").strip()
|
|
62
|
+
papyrus_location_uri = str(
|
|
63
|
+
payload.get("papyrusLocationUri") or payload.get("papyrus_location_uri") or ""
|
|
64
|
+
).strip()
|
|
65
|
+
if not web_path and not papyrus_location_uri:
|
|
66
|
+
return None
|
|
67
|
+
context = dict(payload)
|
|
68
|
+
if web_path:
|
|
69
|
+
context["webPath"] = web_path
|
|
70
|
+
if papyrus_location_uri:
|
|
71
|
+
context["papyrusLocationUri"] = papyrus_location_uri
|
|
72
|
+
return context
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _execute_tactus(arguments: dict[str, Any], web_ui_context: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
76
|
+
try:
|
|
77
|
+
from papyrus_newsroom.tactus_runtime import execute_tactus_harnessed # type: ignore
|
|
78
|
+
except Exception as exc:
|
|
79
|
+
return _error("runner_unavailable", f"Unable to import papyrus_newsroom.tactus_runtime: {exc}")
|
|
80
|
+
|
|
81
|
+
try:
|
|
82
|
+
return execute_tactus_harnessed(
|
|
83
|
+
str(arguments.get("tactus") or ""),
|
|
84
|
+
harness=str(arguments.get("harness") or "raw"),
|
|
85
|
+
assignment_id=str(arguments.get("assignment_id") or arguments.get("assignmentId") or ""),
|
|
86
|
+
assignment_item_json=str(arguments.get("assignment_item_json") or arguments.get("assignmentItemJson") or ""),
|
|
87
|
+
corpus_key=str(arguments.get("corpus_key") or arguments.get("corpusKey") or ""),
|
|
88
|
+
max_evidence_items=int(arguments.get("max_evidence_items") or arguments.get("maxEvidenceItems") or 20),
|
|
89
|
+
research_mode=str(arguments.get("research_mode") or arguments.get("researchMode") or ""),
|
|
90
|
+
web_ui_context=web_ui_context,
|
|
91
|
+
)
|
|
92
|
+
except Exception as exc:
|
|
93
|
+
return _error("tactus_execution_failed", f"Unexpected error: {exc}")
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def main() -> None:
|
|
97
|
+
_ensure_graphql_authoring_jwt()
|
|
98
|
+
raw = sys.stdin.read()
|
|
99
|
+
payload = json.loads(raw or "{}")
|
|
100
|
+
mode = str(payload.get("mode") or "").strip()
|
|
101
|
+
|
|
102
|
+
if mode == "docs_index":
|
|
103
|
+
print(json.dumps(_docs_index()))
|
|
104
|
+
return
|
|
105
|
+
|
|
106
|
+
if mode == "execute_tactus":
|
|
107
|
+
args = payload.get("arguments") if isinstance(payload.get("arguments"), dict) else {}
|
|
108
|
+
web_ui_context = _coerce_web_ui_context(payload.get("webUi") or payload.get("web_ui"))
|
|
109
|
+
print(json.dumps(_execute_tactus(args, web_ui_context=web_ui_context)))
|
|
110
|
+
return
|
|
111
|
+
|
|
112
|
+
print(json.dumps(_error("invalid_mode", f"Unsupported mode: {mode}")))
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
if __name__ == "__main__":
|
|
116
|
+
main()
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { Duration, NestedStack, Stack } from "aws-cdk-lib";
|
|
2
|
+
import { Platform } from "aws-cdk-lib/aws-ecr-assets";
|
|
3
|
+
import { Repository } from "aws-cdk-lib/aws-ecr";
|
|
4
|
+
import { Effect, PolicyStatement } from "aws-cdk-lib/aws-iam";
|
|
5
|
+
import { Architecture, CfnEventSourceMapping, DockerImageCode, DockerImageFunction } from "aws-cdk-lib/aws-lambda";
|
|
6
|
+
export class ConsoleChatResponderStack extends NestedStack {
|
|
7
|
+
responderFunction;
|
|
8
|
+
constructor(scope, id, props) {
|
|
9
|
+
super(scope, id, props);
|
|
10
|
+
const responseTarget = props.responseTarget?.trim() || "cloud";
|
|
11
|
+
const environment = {
|
|
12
|
+
PAPYRUS_MESSAGE_TABLE_NAME: props.messageTable.tableName,
|
|
13
|
+
PAPYRUS_MESSAGE_THREAD_TABLE_NAME: props.threadTable.tableName,
|
|
14
|
+
PAPYRUS_MESSAGE_THREAD_SEQUENCE_INDEX_NAME: "messagesByThreadSequence",
|
|
15
|
+
PAPYRUS_GRAPHQL_ENDPOINT: props.graphqlEndpoint,
|
|
16
|
+
PAPYRUS_CONSOLE_RESPONSE_TARGET: responseTarget,
|
|
17
|
+
PAPYRUS_CONSOLE_MODEL: props.model?.trim() || process.env.PAPYRUS_CONSOLE_MODEL || "gpt-5-nano",
|
|
18
|
+
PAPYRUS_CONSOLE_CONTEXT_CACHE_ROOT: "/tmp/papyrus-console/thread-context",
|
|
19
|
+
PAPYRUS_CONSOLE_STATIC_CONTEXT_TTL_SECONDS: process.env.PAPYRUS_CONSOLE_STATIC_CONTEXT_TTL_SECONDS || "900",
|
|
20
|
+
PAPYRUS_EXECUTE_TACTUS_RUNNER: process.env.PAPYRUS_EXECUTE_TACTUS_RUNNER || "/opt/papyrus/execute_tactus_runner.py",
|
|
21
|
+
PAPYRUS_EXECUTE_TACTUS_TIMEOUT_SECONDS: process.env.PAPYRUS_EXECUTE_TACTUS_TIMEOUT_SECONDS || "30",
|
|
22
|
+
PAPYRUS_JWT_ISSUER: process.env.PAPYRUS_JWT_ISSUER || "papyrus-cli",
|
|
23
|
+
PAPYRUS_JWT_SUBJECT: process.env.PAPYRUS_JWT_SUBJECT || "papyrus-cli",
|
|
24
|
+
PAPYRUS_JWT_AUDIENCE: process.env.PAPYRUS_JWT_AUDIENCE || "papyrus-authoring",
|
|
25
|
+
PAPYRUS_JWT_GROUPS: process.env.PAPYRUS_JWT_GROUPS || "editor",
|
|
26
|
+
...(props.amplifySsmEnvConfig ? { AMPLIFY_SSM_ENV_CONFIG: props.amplifySsmEnvConfig } : {}),
|
|
27
|
+
};
|
|
28
|
+
const imageUri = props.prebuiltImageUri?.trim() || process.env.PAPYRUS_CONSOLE_RESPONDER_IMAGE_URI?.trim() || "";
|
|
29
|
+
const allowLocalImageBuild = parseBooleanEnv(process.env.PAPYRUS_CONSOLE_RESPONDER_ALLOW_LOCAL_BUILD);
|
|
30
|
+
let code;
|
|
31
|
+
if (imageUri) {
|
|
32
|
+
const { repositoryName, tagOrDigest } = parseEcrImageUri(imageUri);
|
|
33
|
+
assertArm64ImageUri(imageUri, tagOrDigest);
|
|
34
|
+
const repository = Repository.fromRepositoryName(this, "ConsoleChatResponderRepository", repositoryName);
|
|
35
|
+
code = DockerImageCode.fromEcr(repository, { tagOrDigest });
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
if (!allowLocalImageBuild) {
|
|
39
|
+
throw new Error("ConsoleChatResponder requires PAPYRUS_CONSOLE_RESPONDER_IMAGE_URI (prebuilt ECR image). "
|
|
40
|
+
+ "Set PAPYRUS_CONSOLE_RESPONDER_ALLOW_LOCAL_BUILD=true only when intentionally using local Docker image builds.");
|
|
41
|
+
}
|
|
42
|
+
code = DockerImageCode.fromImageAsset(props.projectRoot, {
|
|
43
|
+
file: "amplify/functions/console-chat-responder/Dockerfile",
|
|
44
|
+
platform: Platform.LINUX_ARM64,
|
|
45
|
+
exclude: [
|
|
46
|
+
".git",
|
|
47
|
+
".env",
|
|
48
|
+
".env.*",
|
|
49
|
+
".next",
|
|
50
|
+
".amplify",
|
|
51
|
+
"node_modules",
|
|
52
|
+
"coverage",
|
|
53
|
+
"dist",
|
|
54
|
+
"out",
|
|
55
|
+
"corpora",
|
|
56
|
+
".papyrus-runs",
|
|
57
|
+
"amplify_outputs.json",
|
|
58
|
+
"amplify_outputs.json.*",
|
|
59
|
+
"amplify/functions/console-chat-responder/target",
|
|
60
|
+
],
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
this.responderFunction = new DockerImageFunction(this, "ConsoleChatResponderFunction", {
|
|
64
|
+
code,
|
|
65
|
+
architecture: Architecture.ARM_64,
|
|
66
|
+
timeout: Duration.minutes(2),
|
|
67
|
+
memorySize: 1024,
|
|
68
|
+
environment,
|
|
69
|
+
description: "Rust responder for Papyrus editor console chat Message stream",
|
|
70
|
+
});
|
|
71
|
+
new CfnEventSourceMapping(this, "ConsoleResponderMessageStreamMapping", {
|
|
72
|
+
batchSize: 1,
|
|
73
|
+
eventSourceArn: props.messageStreamArn,
|
|
74
|
+
functionName: this.responderFunction.functionArn,
|
|
75
|
+
functionResponseTypes: ["ReportBatchItemFailures"],
|
|
76
|
+
maximumRetryAttempts: 2,
|
|
77
|
+
startingPosition: "LATEST",
|
|
78
|
+
});
|
|
79
|
+
props.messageTable.grantReadWriteData(this.responderFunction);
|
|
80
|
+
props.threadTable.grantReadWriteData(this.responderFunction);
|
|
81
|
+
this.responderFunction.addToRolePolicy(new PolicyStatement({
|
|
82
|
+
effect: Effect.ALLOW,
|
|
83
|
+
actions: ["dynamodb:Query"],
|
|
84
|
+
resources: [
|
|
85
|
+
`${props.messageTable.tableArn}/index/*`,
|
|
86
|
+
`${props.threadTable.tableArn}/index/*`,
|
|
87
|
+
],
|
|
88
|
+
}));
|
|
89
|
+
const stack = Stack.of(this);
|
|
90
|
+
// execute_tactus_runner mints PAPYRUS_GRAPHQL_JWT via boto3 get_parameters; branch policies
|
|
91
|
+
// often grant GetParameters on path prefixes, not GetParameter per secret name.
|
|
92
|
+
this.responderFunction.addToRolePolicy(new PolicyStatement({
|
|
93
|
+
effect: Effect.ALLOW,
|
|
94
|
+
actions: ["ssm:GetParameter", "ssm:GetParameters"],
|
|
95
|
+
resources: [
|
|
96
|
+
`arn:aws:ssm:${stack.region}:${stack.account}:parameter/amplify/papyrus/*`,
|
|
97
|
+
`arn:aws:ssm:${stack.region}:${stack.account}:parameter/amplify/shared/papyrus/*`,
|
|
98
|
+
`arn:aws:ssm:${stack.region}:${stack.account}:parameter/amplify/shared/*`,
|
|
99
|
+
`arn:aws:ssm:${stack.region}:${stack.account}:parameter/amplify/dbsyytcm9drqa/*`,
|
|
100
|
+
`arn:aws:ssm:${stack.region}:${stack.account}:parameter/amplify/shared/dbsyytcm9drqa/*`,
|
|
101
|
+
],
|
|
102
|
+
}));
|
|
103
|
+
this.responderFunction.addToRolePolicy(new PolicyStatement({
|
|
104
|
+
effect: Effect.ALLOW,
|
|
105
|
+
actions: ["appsync:GraphQL"],
|
|
106
|
+
resources: ["*"],
|
|
107
|
+
}));
|
|
108
|
+
this.responderFunction.addToRolePolicy(new PolicyStatement({
|
|
109
|
+
effect: Effect.ALLOW,
|
|
110
|
+
actions: [
|
|
111
|
+
"dynamodb:DescribeStream",
|
|
112
|
+
"dynamodb:GetRecords",
|
|
113
|
+
"dynamodb:GetShardIterator",
|
|
114
|
+
"dynamodb:ListStreams",
|
|
115
|
+
],
|
|
116
|
+
resources: [props.messageStreamArn],
|
|
117
|
+
}));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
function parseBooleanEnv(value) {
|
|
121
|
+
if (!value)
|
|
122
|
+
return false;
|
|
123
|
+
return ["1", "true", "yes", "on"].includes(value.trim().toLowerCase());
|
|
124
|
+
}
|
|
125
|
+
function parseEcrImageUri(imageUri) {
|
|
126
|
+
const trimmed = imageUri.trim();
|
|
127
|
+
const hostAndPath = trimmed.split("/");
|
|
128
|
+
if (hostAndPath.length < 2) {
|
|
129
|
+
throw new Error(`Invalid PAPYRUS_CONSOLE_RESPONDER_IMAGE_URI: ${trimmed}`);
|
|
130
|
+
}
|
|
131
|
+
const imagePath = hostAndPath.slice(1).join("/");
|
|
132
|
+
if (imagePath.includes("@sha256:")) {
|
|
133
|
+
const [repositoryName, digest] = imagePath.split("@", 2);
|
|
134
|
+
if (!repositoryName || !digest) {
|
|
135
|
+
throw new Error(`Invalid PAPYRUS_CONSOLE_RESPONDER_IMAGE_URI digest form: ${trimmed}`);
|
|
136
|
+
}
|
|
137
|
+
return { repositoryName, tagOrDigest: digest };
|
|
138
|
+
}
|
|
139
|
+
const colonIndex = imagePath.lastIndexOf(":");
|
|
140
|
+
if (colonIndex <= 0 || colonIndex === imagePath.length - 1) {
|
|
141
|
+
throw new Error(`PAPYRUS_CONSOLE_RESPONDER_IMAGE_URI must include a tag (repo:tag) or digest: ${trimmed}`);
|
|
142
|
+
}
|
|
143
|
+
return {
|
|
144
|
+
repositoryName: imagePath.slice(0, colonIndex),
|
|
145
|
+
tagOrDigest: imagePath.slice(colonIndex + 1),
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
function assertArm64ImageUri(imageUri, tagOrDigest) {
|
|
149
|
+
const normalized = `${imageUri} ${tagOrDigest}`.toLowerCase();
|
|
150
|
+
if (normalized.includes("amd64") || normalized.includes("x86_64")) {
|
|
151
|
+
throw new Error(`PAPYRUS_CONSOLE_RESPONDER_IMAGE_URI must reference an ARM64 image, but received: ${imageUri}`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { Duration, NestedStack, Stack, type NestedStackProps } from "aws-cdk-lib";
|
|
2
|
+
import { Platform } from "aws-cdk-lib/aws-ecr-assets";
|
|
3
|
+
import { Repository } from "aws-cdk-lib/aws-ecr";
|
|
4
|
+
import { Effect, PolicyStatement } from "aws-cdk-lib/aws-iam";
|
|
5
|
+
import { Architecture, CfnEventSourceMapping, DockerImageCode, DockerImageFunction } from "aws-cdk-lib/aws-lambda";
|
|
6
|
+
import type { ITable } from "aws-cdk-lib/aws-dynamodb";
|
|
7
|
+
import { Construct } from "constructs";
|
|
8
|
+
|
|
9
|
+
export type ConsoleChatResponderStackProps = NestedStackProps & {
|
|
10
|
+
messageTable: ITable;
|
|
11
|
+
messageStreamArn: string;
|
|
12
|
+
threadTable: ITable;
|
|
13
|
+
projectRoot: string;
|
|
14
|
+
graphqlEndpoint: string;
|
|
15
|
+
amplifySsmEnvConfig?: string;
|
|
16
|
+
responseTarget?: string;
|
|
17
|
+
model?: string;
|
|
18
|
+
prebuiltImageUri?: string;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export class ConsoleChatResponderStack extends NestedStack {
|
|
22
|
+
public readonly responderFunction: DockerImageFunction;
|
|
23
|
+
|
|
24
|
+
constructor(scope: Construct, id: string, props: ConsoleChatResponderStackProps) {
|
|
25
|
+
super(scope, id, props);
|
|
26
|
+
|
|
27
|
+
const responseTarget = props.responseTarget?.trim() || "cloud";
|
|
28
|
+
const environment: Record<string, string> = {
|
|
29
|
+
PAPYRUS_MESSAGE_TABLE_NAME: props.messageTable.tableName,
|
|
30
|
+
PAPYRUS_MESSAGE_THREAD_TABLE_NAME: props.threadTable.tableName,
|
|
31
|
+
PAPYRUS_MESSAGE_THREAD_SEQUENCE_INDEX_NAME: "messagesByThreadSequence",
|
|
32
|
+
PAPYRUS_GRAPHQL_ENDPOINT: props.graphqlEndpoint,
|
|
33
|
+
PAPYRUS_CONSOLE_RESPONSE_TARGET: responseTarget,
|
|
34
|
+
PAPYRUS_CONSOLE_MODEL: props.model?.trim() || process.env.PAPYRUS_CONSOLE_MODEL || "gpt-5-nano",
|
|
35
|
+
PAPYRUS_CONSOLE_CONTEXT_CACHE_ROOT: "/tmp/papyrus-console/thread-context",
|
|
36
|
+
PAPYRUS_CONSOLE_STATIC_CONTEXT_TTL_SECONDS: process.env.PAPYRUS_CONSOLE_STATIC_CONTEXT_TTL_SECONDS || "900",
|
|
37
|
+
PAPYRUS_EXECUTE_TACTUS_RUNNER: process.env.PAPYRUS_EXECUTE_TACTUS_RUNNER || "/opt/papyrus/execute_tactus_runner.py",
|
|
38
|
+
PAPYRUS_EXECUTE_TACTUS_TIMEOUT_SECONDS: process.env.PAPYRUS_EXECUTE_TACTUS_TIMEOUT_SECONDS || "30",
|
|
39
|
+
PAPYRUS_JWT_ISSUER: process.env.PAPYRUS_JWT_ISSUER || "papyrus-cli",
|
|
40
|
+
PAPYRUS_JWT_SUBJECT: process.env.PAPYRUS_JWT_SUBJECT || "papyrus-cli",
|
|
41
|
+
PAPYRUS_JWT_AUDIENCE: process.env.PAPYRUS_JWT_AUDIENCE || "papyrus-authoring",
|
|
42
|
+
PAPYRUS_JWT_GROUPS: process.env.PAPYRUS_JWT_GROUPS || "editor",
|
|
43
|
+
...(props.amplifySsmEnvConfig ? { AMPLIFY_SSM_ENV_CONFIG: props.amplifySsmEnvConfig } : {}),
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const imageUri = props.prebuiltImageUri?.trim() || process.env.PAPYRUS_CONSOLE_RESPONDER_IMAGE_URI?.trim() || "";
|
|
47
|
+
const allowLocalImageBuild = parseBooleanEnv(process.env.PAPYRUS_CONSOLE_RESPONDER_ALLOW_LOCAL_BUILD);
|
|
48
|
+
let code: DockerImageCode;
|
|
49
|
+
if (imageUri) {
|
|
50
|
+
const { repositoryName, tagOrDigest } = parseEcrImageUri(imageUri);
|
|
51
|
+
assertArm64ImageUri(imageUri, tagOrDigest);
|
|
52
|
+
const repository = Repository.fromRepositoryName(this, "ConsoleChatResponderRepository", repositoryName);
|
|
53
|
+
code = DockerImageCode.fromEcr(repository, { tagOrDigest });
|
|
54
|
+
} else {
|
|
55
|
+
if (!allowLocalImageBuild) {
|
|
56
|
+
throw new Error(
|
|
57
|
+
"ConsoleChatResponder requires PAPYRUS_CONSOLE_RESPONDER_IMAGE_URI (prebuilt ECR image). "
|
|
58
|
+
+ "Set PAPYRUS_CONSOLE_RESPONDER_ALLOW_LOCAL_BUILD=true only when intentionally using local Docker image builds.",
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
code = DockerImageCode.fromImageAsset(props.projectRoot, {
|
|
62
|
+
file: "amplify/functions/console-chat-responder/Dockerfile",
|
|
63
|
+
platform: Platform.LINUX_ARM64,
|
|
64
|
+
exclude: [
|
|
65
|
+
".git",
|
|
66
|
+
".env",
|
|
67
|
+
".env.*",
|
|
68
|
+
".next",
|
|
69
|
+
".amplify",
|
|
70
|
+
"node_modules",
|
|
71
|
+
"coverage",
|
|
72
|
+
"dist",
|
|
73
|
+
"out",
|
|
74
|
+
"corpora",
|
|
75
|
+
".papyrus-runs",
|
|
76
|
+
"amplify_outputs.json",
|
|
77
|
+
"amplify_outputs.json.*",
|
|
78
|
+
"amplify/functions/console-chat-responder/target",
|
|
79
|
+
],
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
this.responderFunction = new DockerImageFunction(this, "ConsoleChatResponderFunction", {
|
|
84
|
+
code,
|
|
85
|
+
architecture: Architecture.ARM_64,
|
|
86
|
+
timeout: Duration.minutes(2),
|
|
87
|
+
memorySize: 1024,
|
|
88
|
+
environment,
|
|
89
|
+
description: "Rust responder for Papyrus editor console chat Message stream",
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
new CfnEventSourceMapping(this, "ConsoleResponderMessageStreamMapping", {
|
|
93
|
+
batchSize: 1,
|
|
94
|
+
eventSourceArn: props.messageStreamArn,
|
|
95
|
+
functionName: this.responderFunction.functionArn,
|
|
96
|
+
functionResponseTypes: ["ReportBatchItemFailures"],
|
|
97
|
+
maximumRetryAttempts: 2,
|
|
98
|
+
startingPosition: "LATEST",
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
props.messageTable.grantReadWriteData(this.responderFunction);
|
|
102
|
+
props.threadTable.grantReadWriteData(this.responderFunction);
|
|
103
|
+
this.responderFunction.addToRolePolicy(new PolicyStatement({
|
|
104
|
+
effect: Effect.ALLOW,
|
|
105
|
+
actions: ["dynamodb:Query"],
|
|
106
|
+
resources: [
|
|
107
|
+
`${props.messageTable.tableArn}/index/*`,
|
|
108
|
+
`${props.threadTable.tableArn}/index/*`,
|
|
109
|
+
],
|
|
110
|
+
}));
|
|
111
|
+
const stack = Stack.of(this);
|
|
112
|
+
// execute_tactus_runner mints PAPYRUS_GRAPHQL_JWT via boto3 get_parameters; branch policies
|
|
113
|
+
// often grant GetParameters on path prefixes, not GetParameter per secret name.
|
|
114
|
+
this.responderFunction.addToRolePolicy(new PolicyStatement({
|
|
115
|
+
effect: Effect.ALLOW,
|
|
116
|
+
actions: ["ssm:GetParameter", "ssm:GetParameters"],
|
|
117
|
+
resources: [
|
|
118
|
+
`arn:aws:ssm:${stack.region}:${stack.account}:parameter/amplify/papyrus/*`,
|
|
119
|
+
`arn:aws:ssm:${stack.region}:${stack.account}:parameter/amplify/shared/papyrus/*`,
|
|
120
|
+
`arn:aws:ssm:${stack.region}:${stack.account}:parameter/amplify/shared/*`,
|
|
121
|
+
`arn:aws:ssm:${stack.region}:${stack.account}:parameter/amplify/dbsyytcm9drqa/*`,
|
|
122
|
+
`arn:aws:ssm:${stack.region}:${stack.account}:parameter/amplify/shared/dbsyytcm9drqa/*`,
|
|
123
|
+
],
|
|
124
|
+
}));
|
|
125
|
+
this.responderFunction.addToRolePolicy(new PolicyStatement({
|
|
126
|
+
effect: Effect.ALLOW,
|
|
127
|
+
actions: ["appsync:GraphQL"],
|
|
128
|
+
resources: ["*"],
|
|
129
|
+
}));
|
|
130
|
+
this.responderFunction.addToRolePolicy(new PolicyStatement({
|
|
131
|
+
effect: Effect.ALLOW,
|
|
132
|
+
actions: [
|
|
133
|
+
"dynamodb:DescribeStream",
|
|
134
|
+
"dynamodb:GetRecords",
|
|
135
|
+
"dynamodb:GetShardIterator",
|
|
136
|
+
"dynamodb:ListStreams",
|
|
137
|
+
],
|
|
138
|
+
resources: [props.messageStreamArn],
|
|
139
|
+
}));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function parseBooleanEnv(value: string | undefined): boolean {
|
|
144
|
+
if (!value) return false;
|
|
145
|
+
return ["1", "true", "yes", "on"].includes(value.trim().toLowerCase());
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function parseEcrImageUri(imageUri: string): { repositoryName: string; tagOrDigest: string } {
|
|
149
|
+
const trimmed = imageUri.trim();
|
|
150
|
+
const hostAndPath = trimmed.split("/");
|
|
151
|
+
if (hostAndPath.length < 2) {
|
|
152
|
+
throw new Error(`Invalid PAPYRUS_CONSOLE_RESPONDER_IMAGE_URI: ${trimmed}`);
|
|
153
|
+
}
|
|
154
|
+
const imagePath = hostAndPath.slice(1).join("/");
|
|
155
|
+
if (imagePath.includes("@sha256:")) {
|
|
156
|
+
const [repositoryName, digest] = imagePath.split("@", 2);
|
|
157
|
+
if (!repositoryName || !digest) {
|
|
158
|
+
throw new Error(`Invalid PAPYRUS_CONSOLE_RESPONDER_IMAGE_URI digest form: ${trimmed}`);
|
|
159
|
+
}
|
|
160
|
+
return { repositoryName, tagOrDigest: digest };
|
|
161
|
+
}
|
|
162
|
+
const colonIndex = imagePath.lastIndexOf(":");
|
|
163
|
+
if (colonIndex <= 0 || colonIndex === imagePath.length - 1) {
|
|
164
|
+
throw new Error(`PAPYRUS_CONSOLE_RESPONDER_IMAGE_URI must include a tag (repo:tag) or digest: ${trimmed}`);
|
|
165
|
+
}
|
|
166
|
+
return {
|
|
167
|
+
repositoryName: imagePath.slice(0, colonIndex),
|
|
168
|
+
tagOrDigest: imagePath.slice(colonIndex + 1),
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function assertArm64ImageUri(imageUri: string, tagOrDigest: string): void {
|
|
173
|
+
const normalized = `${imageUri} ${tagOrDigest}`.toLowerCase();
|
|
174
|
+
if (normalized.includes("amd64") || normalized.includes("x86_64")) {
|
|
175
|
+
throw new Error(
|
|
176
|
+
`PAPYRUS_CONSOLE_RESPONDER_IMAGE_URI must reference an ARM64 image, but received: ${imageUri}`,
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
}
|