@anyshift/backstage-plugin-anyshift 0.1.0-beta.10 → 0.1.0-beta.12
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/CHANGELOG.md +15 -0
- package/README.md +7 -0
- package/dist/api/AnyshiftApi.esm.js +38 -3
- package/dist/api/AnyshiftApi.esm.js.map +1 -1
- package/dist/components/ask/AskAnyshiftCard.esm.js +342 -0
- package/dist/components/ask/AskAnyshiftCard.esm.js.map +1 -0
- package/dist/components/ask/askPresenters.esm.js +1174 -0
- package/dist/components/ask/askPresenters.esm.js.map +1 -0
- package/dist/components/ask/askScope.esm.js +25 -0
- package/dist/components/ask/askScope.esm.js.map +1 -0
- package/dist/components/ask/askSuggestions.esm.js +41 -0
- package/dist/components/ask/askSuggestions.esm.js.map +1 -0
- package/dist/components/cloud/CloudImpactSection.esm.js +54 -0
- package/dist/components/cloud/CloudImpactSection.esm.js.map +1 -0
- package/dist/components/cloud/CloudRelationshipSections.esm.js +43 -0
- package/dist/components/cloud/CloudRelationshipSections.esm.js.map +1 -0
- package/dist/components/cloud/CloudResourceContent.esm.js +299 -172
- package/dist/components/cloud/CloudResourceContent.esm.js.map +1 -1
- package/dist/components/entity/AnyshiftEntityContent.esm.js +30 -12
- package/dist/components/entity/AnyshiftEntityContent.esm.js.map +1 -1
- package/dist/components/entity/SectionPresenters.esm.js +14 -7
- package/dist/components/entity/SectionPresenters.esm.js.map +1 -1
- package/dist/components/entity/entityIdentity.esm.js +30 -0
- package/dist/components/entity/entityIdentity.esm.js.map +1 -0
- package/dist/components/explorer/AnyshiftExplorerPage.esm.js +8 -3
- package/dist/components/explorer/AnyshiftExplorerPage.esm.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/package.json.esm.js +1 -1
- package/dist/plugin.esm.js +6 -2
- package/dist/plugin.esm.js.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
## 0.1.0-beta.12
|
|
6
|
+
|
|
7
|
+
- Render cloud Resources through shared Cloud and Anyshift evidence cards, with direct catalog
|
|
8
|
+
dependencies, typed potential impact, cloud changes, IaC evidence, and ARN-scoped Ask.
|
|
9
|
+
- Keep cloud evidence separate from workload/Kubernetes safeguards and make absent catalog, change,
|
|
10
|
+
IaC, or impact evidence explicitly unknown rather than healthy.
|
|
11
|
+
|
|
12
|
+
## 0.1.0-beta.11
|
|
13
|
+
|
|
14
|
+
- Add one reusable Ask Anyshift card to the estate dashboard and supported entity pages.
|
|
15
|
+
- Render bounded typed evidence, visible entity scope, effective interpretation, uncertainty, and safe error states.
|
|
16
|
+
- Keep viewer Ask availability independent from operator-only Advanced Query.
|
|
17
|
+
|
|
3
18
|
## 0.1.0-beta.10
|
|
4
19
|
|
|
5
20
|
- Render why-now evidence immediately after the entity overview without claiming causality.
|
package/README.md
CHANGED
|
@@ -10,6 +10,13 @@ Terraform provenance, and drift evidence alongside their stable identity, provid
|
|
|
10
10
|
and catalog relationships. Evidence sections degrade independently, and an unknown drift verdict
|
|
11
11
|
is not presented as drift.
|
|
12
12
|
|
|
13
|
+
When `anyshift.ask.enabled` is true, the same Ask Anyshift card appears on the estate dashboard and
|
|
14
|
+
supported entity pages for every viewer with `anyshift.view`. It is a single-question experience,
|
|
15
|
+
not a chat history. Entity scope is visible and removable, and the effective `For <target>: ...`
|
|
16
|
+
interpretation is shown with each answer. Typed presenters render only supported evidence; missing
|
|
17
|
+
timestamps and missing evidence remain explicitly unknown. Advanced Query stays independently
|
|
18
|
+
controlled by `anyshift.query.enabled` and `anyshift.query`.
|
|
19
|
+
|
|
13
20
|
The package has no legacy frontend entry point. Add the corresponding
|
|
14
21
|
`@anyshift/backstage-plugin-anyshift-backend` package to the Backstage backend and configure one
|
|
15
22
|
Anyshift project. See the [integration guide](../../README.md) for the complete setup.
|
|
@@ -3,6 +3,18 @@ import { createApiRef } from '@backstage/frontend-plugin-api';
|
|
|
3
3
|
const anyshiftApiRef = createApiRef({
|
|
4
4
|
id: "plugin.anyshift.service"
|
|
5
5
|
});
|
|
6
|
+
class AnyshiftRequestError extends Error {
|
|
7
|
+
constructor(status, code, message, retryAfterSeconds) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.status = status;
|
|
10
|
+
this.code = code;
|
|
11
|
+
this.retryAfterSeconds = retryAfterSeconds;
|
|
12
|
+
this.name = "AnyshiftRequestError";
|
|
13
|
+
}
|
|
14
|
+
status;
|
|
15
|
+
code;
|
|
16
|
+
retryAfterSeconds;
|
|
17
|
+
}
|
|
6
18
|
class AnyshiftClient {
|
|
7
19
|
discoveryApi;
|
|
8
20
|
fetchApi;
|
|
@@ -10,6 +22,13 @@ class AnyshiftClient {
|
|
|
10
22
|
this.discoveryApi = options.discoveryApi;
|
|
11
23
|
this.fetchApi = options.fetchApi;
|
|
12
24
|
}
|
|
25
|
+
async ask(request) {
|
|
26
|
+
return this.request("/ask", {
|
|
27
|
+
method: "POST",
|
|
28
|
+
headers: { "content-type": "application/json" },
|
|
29
|
+
body: JSON.stringify(request)
|
|
30
|
+
});
|
|
31
|
+
}
|
|
13
32
|
async capabilities() {
|
|
14
33
|
return this.get("/capabilities");
|
|
15
34
|
}
|
|
@@ -72,13 +91,29 @@ class AnyshiftClient {
|
|
|
72
91
|
payload = {};
|
|
73
92
|
}
|
|
74
93
|
if (!response.ok) {
|
|
75
|
-
|
|
76
|
-
|
|
94
|
+
const error = errorEnvelope(payload);
|
|
95
|
+
throw new AnyshiftRequestError(
|
|
96
|
+
response.status,
|
|
97
|
+
error.code,
|
|
98
|
+
error.message ?? `Anyshift request failed (${response.status})`,
|
|
99
|
+
error.retryAfterSeconds
|
|
77
100
|
);
|
|
78
101
|
}
|
|
79
102
|
return payload;
|
|
80
103
|
}
|
|
81
104
|
}
|
|
105
|
+
function errorEnvelope(payload) {
|
|
106
|
+
const error = payload && typeof payload === "object" && "error" in payload ? payload.error : void 0;
|
|
107
|
+
if (!error || typeof error !== "object") {
|
|
108
|
+
return { code: "request_failed" };
|
|
109
|
+
}
|
|
110
|
+
const { code, message, retryAfterSeconds } = error;
|
|
111
|
+
return {
|
|
112
|
+
code: typeof code === "string" ? code : "request_failed",
|
|
113
|
+
message: typeof message === "string" && message ? message : void 0,
|
|
114
|
+
retryAfterSeconds: typeof retryAfterSeconds === "number" && Number.isFinite(retryAfterSeconds) ? retryAfterSeconds : void 0
|
|
115
|
+
};
|
|
116
|
+
}
|
|
82
117
|
function briefQuery(options) {
|
|
83
118
|
const params = new URLSearchParams();
|
|
84
119
|
if (options.namespace) params.set("namespace", options.namespace);
|
|
@@ -93,5 +128,5 @@ function briefQuery(options) {
|
|
|
93
128
|
return query ? `?${query}` : "";
|
|
94
129
|
}
|
|
95
130
|
|
|
96
|
-
export { AnyshiftClient, anyshiftApiRef };
|
|
131
|
+
export { AnyshiftClient, AnyshiftRequestError, anyshiftApiRef };
|
|
97
132
|
//# sourceMappingURL=AnyshiftApi.esm.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AnyshiftApi.esm.js","sources":["../../src/api/AnyshiftApi.ts"],"sourcesContent":["import {\n createApiRef,\n type DiscoveryApi,\n type FetchApi,\n} from '@backstage/frontend-plugin-api';\nimport type {\n BriefSectionName,\n BriefSectionResponse,\n AnyshiftCapabilities,\n CatalogSummary,\n CatalogSyncReport,\n CloudResourceBrief,\n DatadogSignals,\n EstateInsights,\n QueryResult,\n ServiceBrief,\n} from './types';\n\nexport interface AnyshiftApi {\n capabilities(): Promise<AnyshiftCapabilities>;\n brief(\n kind: string,\n target: string,\n options?: BriefRequestOptions,\n ): Promise<ServiceBrief>;\n briefSection(\n kind: string,\n target: string,\n section: BriefSectionName,\n options?: BriefRequestOptions,\n ): Promise<BriefSectionResponse>;\n insights(options?: { refresh?: boolean }): Promise<EstateInsights>;\n datadogSignals(options?: { refresh?: boolean }): Promise<DatadogSignals>;\n cloudResourceBrief(\n resource: string,\n options?: CloudResourceBriefRequestOptions,\n ): Promise<CloudResourceBrief>;\n inventory(\n type: string,\n options?: { limit?: number; offset?: number },\n ): Promise<CatalogSummary>;\n query(sql: string): Promise<QueryResult>;\n reconcileCatalog(): Promise<CatalogSyncAccepted>;\n}\n\nexport interface BriefRequestOptions {\n namespace?: string;\n refresh?: boolean;\n configurationEndpoints?: string;\n configurationDependencies?: string;\n configurationTarget?: string;\n}\n\nexport interface CloudResourceBriefRequestOptions {\n since?: string;\n cursor?: string;\n diff?: boolean;\n refresh?: boolean;\n}\n\nexport interface CatalogSyncAccepted {\n accepted: true;\n acceptedAt: string;\n previous?: CatalogSyncReport;\n}\n\nexport const anyshiftApiRef = createApiRef<AnyshiftApi>({\n id: 'plugin.anyshift.service',\n});\n\nexport class AnyshiftClient implements AnyshiftApi {\n private readonly discoveryApi: DiscoveryApi;\n private readonly fetchApi: FetchApi;\n\n constructor(options: { discoveryApi: DiscoveryApi; fetchApi: FetchApi }) {\n this.discoveryApi = options.discoveryApi;\n this.fetchApi = options.fetchApi;\n }\n\n async capabilities(): Promise<AnyshiftCapabilities> {\n return this.get('/capabilities');\n }\n\n async brief(\n kind: string,\n target: string,\n options: BriefRequestOptions = {},\n ): Promise<ServiceBrief> {\n return this.get(\n `/brief/${encodeURIComponent(kind)}/${encodeURIComponent(\n target,\n )}${briefQuery(options)}`,\n );\n }\n\n async briefSection(\n kind: string,\n target: string,\n section: BriefSectionName,\n options: BriefRequestOptions = {},\n ): Promise<BriefSectionResponse> {\n return this.get(\n `/brief/${encodeURIComponent(kind)}/${encodeURIComponent(\n target,\n )}/sections/${encodeURIComponent(section)}${briefQuery(options)}`,\n );\n }\n\n async insights(options: { refresh?: boolean } = {}): Promise<EstateInsights> {\n return this.get(`/insights${options.refresh ? '?refresh=1' : ''}`);\n }\n\n async datadogSignals(\n options: { refresh?: boolean } = {},\n ): Promise<DatadogSignals> {\n return this.get(`/signals/datadog${options.refresh ? '?refresh=1' : ''}`);\n }\n\n async cloudResourceBrief(\n resource: string,\n options: CloudResourceBriefRequestOptions = {},\n ): Promise<CloudResourceBrief> {\n const params = new URLSearchParams({ resource });\n if (options.since) params.set('since', options.since);\n if (options.cursor) params.set('cursor', options.cursor);\n if (options.diff !== undefined) params.set('diff', String(options.diff));\n if (options.refresh) params.set('refresh', '1');\n return this.get(`/cloud/resources/brief?${params}`);\n }\n\n async inventory(\n type: string,\n options: { limit?: number; offset?: number } = {},\n ): Promise<CatalogSummary> {\n const params = new URLSearchParams({ type });\n if (options.limit !== undefined) params.set('limit', String(options.limit));\n if (options.offset !== undefined)\n params.set('offset', String(options.offset));\n return this.get(`/inventory?${params}`);\n }\n\n async query(sql: string): Promise<QueryResult> {\n return this.request('/query', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ sql }),\n });\n }\n\n async reconcileCatalog(): Promise<CatalogSyncAccepted> {\n return this.request('/anyshift/reconcile', { method: 'POST' }, 'catalog');\n }\n\n private async get<T>(path: string): Promise<T> {\n return this.request(path, { method: 'GET' });\n }\n\n private async request<T>(\n path: string,\n init: RequestInit,\n pluginId = 'anyshift',\n ): Promise<T> {\n const baseUrl = await this.discoveryApi.getBaseUrl(pluginId);\n const response = await this.fetchApi.fetch(`${baseUrl}${path}`, init);\n const text = await response.text();\n let payload: any;\n try {\n payload = text ? JSON.parse(text) : {};\n } catch {\n payload = {};\n }\n if (!response.ok) {\n throw new Error(\n payload?.error?.message ||\n `Anyshift request failed (${response.status})`,\n );\n }\n return payload as T;\n }\n}\n\nfunction briefQuery(options: BriefRequestOptions): string {\n const params = new URLSearchParams();\n if (options.namespace) params.set('namespace', options.namespace);\n if (options.refresh) params.set('refresh', '1');\n if (options.configurationEndpoints)\n params.set('configurationEndpoints', options.configurationEndpoints);\n if (options.configurationDependencies)\n params.set('configurationDependencies', options.configurationDependencies);\n if (options.configurationTarget)\n params.set('configurationTarget', options.configurationTarget);\n const query = params.toString();\n return query ? `?${query}` : '';\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AnyshiftApi.esm.js","sources":["../../src/api/AnyshiftApi.ts"],"sourcesContent":["import {\n createApiRef,\n type DiscoveryApi,\n type FetchApi,\n} from '@backstage/frontend-plugin-api';\nimport type {\n BriefSectionName,\n BriefSectionResponse,\n AnyshiftCapabilities,\n AskAnyshiftRequest,\n AskAnyshiftResponse,\n CatalogSummary,\n CatalogSyncReport,\n CloudResourceBrief,\n DatadogSignals,\n EstateInsights,\n QueryResult,\n ServiceBrief,\n} from './types';\n\nexport interface AnyshiftApi {\n ask(request: AskAnyshiftRequest): Promise<AskAnyshiftResponse>;\n capabilities(): Promise<AnyshiftCapabilities>;\n brief(\n kind: string,\n target: string,\n options?: BriefRequestOptions,\n ): Promise<ServiceBrief>;\n briefSection(\n kind: string,\n target: string,\n section: BriefSectionName,\n options?: BriefRequestOptions,\n ): Promise<BriefSectionResponse>;\n insights(options?: { refresh?: boolean }): Promise<EstateInsights>;\n datadogSignals(options?: { refresh?: boolean }): Promise<DatadogSignals>;\n cloudResourceBrief(\n resource: string,\n options?: CloudResourceBriefRequestOptions,\n ): Promise<CloudResourceBrief>;\n inventory(\n type: string,\n options?: { limit?: number; offset?: number },\n ): Promise<CatalogSummary>;\n query(sql: string): Promise<QueryResult>;\n reconcileCatalog(): Promise<CatalogSyncAccepted>;\n}\n\nexport interface BriefRequestOptions {\n namespace?: string;\n refresh?: boolean;\n configurationEndpoints?: string;\n configurationDependencies?: string;\n configurationTarget?: string;\n}\n\nexport interface CloudResourceBriefRequestOptions {\n since?: string;\n cursor?: string;\n diff?: boolean;\n refresh?: boolean;\n}\n\nexport interface CatalogSyncAccepted {\n accepted: true;\n acceptedAt: string;\n previous?: CatalogSyncReport;\n}\n\nexport const anyshiftApiRef = createApiRef<AnyshiftApi>({\n id: 'plugin.anyshift.service',\n});\n\nexport class AnyshiftRequestError extends Error {\n constructor(\n readonly status: number,\n readonly code: string,\n message: string,\n readonly retryAfterSeconds?: number,\n ) {\n super(message);\n this.name = 'AnyshiftRequestError';\n }\n}\n\nexport class AnyshiftClient implements AnyshiftApi {\n private readonly discoveryApi: DiscoveryApi;\n private readonly fetchApi: FetchApi;\n\n constructor(options: { discoveryApi: DiscoveryApi; fetchApi: FetchApi }) {\n this.discoveryApi = options.discoveryApi;\n this.fetchApi = options.fetchApi;\n }\n\n async ask(request: AskAnyshiftRequest): Promise<AskAnyshiftResponse> {\n return this.request('/ask', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(request),\n });\n }\n\n async capabilities(): Promise<AnyshiftCapabilities> {\n return this.get('/capabilities');\n }\n\n async brief(\n kind: string,\n target: string,\n options: BriefRequestOptions = {},\n ): Promise<ServiceBrief> {\n return this.get(\n `/brief/${encodeURIComponent(kind)}/${encodeURIComponent(\n target,\n )}${briefQuery(options)}`,\n );\n }\n\n async briefSection(\n kind: string,\n target: string,\n section: BriefSectionName,\n options: BriefRequestOptions = {},\n ): Promise<BriefSectionResponse> {\n return this.get(\n `/brief/${encodeURIComponent(kind)}/${encodeURIComponent(\n target,\n )}/sections/${encodeURIComponent(section)}${briefQuery(options)}`,\n );\n }\n\n async insights(options: { refresh?: boolean } = {}): Promise<EstateInsights> {\n return this.get(`/insights${options.refresh ? '?refresh=1' : ''}`);\n }\n\n async datadogSignals(\n options: { refresh?: boolean } = {},\n ): Promise<DatadogSignals> {\n return this.get(`/signals/datadog${options.refresh ? '?refresh=1' : ''}`);\n }\n\n async cloudResourceBrief(\n resource: string,\n options: CloudResourceBriefRequestOptions = {},\n ): Promise<CloudResourceBrief> {\n const params = new URLSearchParams({ resource });\n if (options.since) params.set('since', options.since);\n if (options.cursor) params.set('cursor', options.cursor);\n if (options.diff !== undefined) params.set('diff', String(options.diff));\n if (options.refresh) params.set('refresh', '1');\n return this.get(`/cloud/resources/brief?${params}`);\n }\n\n async inventory(\n type: string,\n options: { limit?: number; offset?: number } = {},\n ): Promise<CatalogSummary> {\n const params = new URLSearchParams({ type });\n if (options.limit !== undefined) params.set('limit', String(options.limit));\n if (options.offset !== undefined)\n params.set('offset', String(options.offset));\n return this.get(`/inventory?${params}`);\n }\n\n async query(sql: string): Promise<QueryResult> {\n return this.request('/query', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ sql }),\n });\n }\n\n async reconcileCatalog(): Promise<CatalogSyncAccepted> {\n return this.request('/anyshift/reconcile', { method: 'POST' }, 'catalog');\n }\n\n private async get<T>(path: string): Promise<T> {\n return this.request(path, { method: 'GET' });\n }\n\n private async request<T>(\n path: string,\n init: RequestInit,\n pluginId = 'anyshift',\n ): Promise<T> {\n const baseUrl = await this.discoveryApi.getBaseUrl(pluginId);\n const response = await this.fetchApi.fetch(`${baseUrl}${path}`, init);\n const text = await response.text();\n let payload: unknown;\n try {\n payload = text ? JSON.parse(text) : {};\n } catch {\n payload = {};\n }\n if (!response.ok) {\n const error = errorEnvelope(payload);\n throw new AnyshiftRequestError(\n response.status,\n error.code,\n error.message ?? `Anyshift request failed (${response.status})`,\n error.retryAfterSeconds,\n );\n }\n return payload as T;\n }\n}\n\nfunction errorEnvelope(payload: unknown): {\n code: string;\n message?: string;\n retryAfterSeconds?: number;\n} {\n const error =\n payload && typeof payload === 'object' && 'error' in payload\n ? (payload as { error?: unknown }).error\n : undefined;\n if (!error || typeof error !== 'object') {\n return { code: 'request_failed' };\n }\n\n const { code, message, retryAfterSeconds } = error as {\n code?: unknown;\n message?: unknown;\n retryAfterSeconds?: unknown;\n };\n return {\n code: typeof code === 'string' ? code : 'request_failed',\n message: typeof message === 'string' && message ? message : undefined,\n retryAfterSeconds:\n typeof retryAfterSeconds === 'number' &&\n Number.isFinite(retryAfterSeconds)\n ? retryAfterSeconds\n : undefined,\n };\n}\n\nfunction briefQuery(options: BriefRequestOptions): string {\n const params = new URLSearchParams();\n if (options.namespace) params.set('namespace', options.namespace);\n if (options.refresh) params.set('refresh', '1');\n if (options.configurationEndpoints)\n params.set('configurationEndpoints', options.configurationEndpoints);\n if (options.configurationDependencies)\n params.set('configurationDependencies', options.configurationDependencies);\n if (options.configurationTarget)\n params.set('configurationTarget', options.configurationTarget);\n const query = params.toString();\n return query ? `?${query}` : '';\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
|
+
import { Card, CardContent, Typography, Box, Chip, Button, TextField, CircularProgress } from '@material-ui/core';
|
|
3
|
+
import { useApi } from '@backstage/frontend-plugin-api';
|
|
4
|
+
import { useRef, useState, useLayoutEffect } from 'react';
|
|
5
|
+
import { anyshiftApiRef, AnyshiftRequestError } from '../../api/AnyshiftApi.esm.js';
|
|
6
|
+
import { scopedSuggestions, estateSuggestions, followUpsForIntent } from './askSuggestions.esm.js';
|
|
7
|
+
import { presentAskResult, AskResultPresenter } from './askPresenters.esm.js';
|
|
8
|
+
|
|
9
|
+
const MAX_QUESTION_CODE_POINTS = 500;
|
|
10
|
+
const MAX_SCOPE_TARGET_CODE_POINTS = 200;
|
|
11
|
+
const IDLE_HELPER_TEXT = "Read-only answers from your connected graph. Do not include secrets.";
|
|
12
|
+
const AskAnyshiftCard = ({ context, scope }) => {
|
|
13
|
+
const api = useApi(anyshiftApiRef);
|
|
14
|
+
const validPropScope = context === "entity" && isValidScope(scope) ? scope : void 0;
|
|
15
|
+
const propScopeIdentity = context === "entity" && scope?.entityRef.trim() ? scope.entityRef : void 0;
|
|
16
|
+
const mountedIdentity = context === "estate" ? "estate" : `entity:${propScopeIdentity ?? ""}`;
|
|
17
|
+
const previousMountedIdentity = useRef(mountedIdentity);
|
|
18
|
+
const requestGeneration = useRef(0);
|
|
19
|
+
const activeRequest = useRef();
|
|
20
|
+
const [question, setQuestion] = useState("");
|
|
21
|
+
const [validationError, setValidationError] = useState();
|
|
22
|
+
const [state, setState] = useState({ status: "idle" });
|
|
23
|
+
const [scopeRemoved, setScopeRemoved] = useState(false);
|
|
24
|
+
const localScope = scopeRemoved ? void 0 : validPropScope;
|
|
25
|
+
const restorableScope = validPropScope;
|
|
26
|
+
const requestContext = requestContextKey(
|
|
27
|
+
context,
|
|
28
|
+
propScopeIdentity,
|
|
29
|
+
localScope
|
|
30
|
+
);
|
|
31
|
+
const previousRequestContext = useRef(requestContext);
|
|
32
|
+
useLayoutEffect(() => {
|
|
33
|
+
if (previousMountedIdentity.current === mountedIdentity) return;
|
|
34
|
+
previousMountedIdentity.current = mountedIdentity;
|
|
35
|
+
requestGeneration.current += 1;
|
|
36
|
+
activeRequest.current = void 0;
|
|
37
|
+
setState({ status: "idle" });
|
|
38
|
+
setValidationError(void 0);
|
|
39
|
+
setScopeRemoved(false);
|
|
40
|
+
}, [mountedIdentity]);
|
|
41
|
+
useLayoutEffect(() => {
|
|
42
|
+
if (previousRequestContext.current === requestContext) return;
|
|
43
|
+
previousRequestContext.current = requestContext;
|
|
44
|
+
requestGeneration.current += 1;
|
|
45
|
+
if (!activeRequest.current) return;
|
|
46
|
+
activeRequest.current = void 0;
|
|
47
|
+
setState(
|
|
48
|
+
(current) => current.status === "loading" ? { status: "idle" } : current
|
|
49
|
+
);
|
|
50
|
+
setValidationError(void 0);
|
|
51
|
+
}, [requestContext]);
|
|
52
|
+
const loading = state.status === "loading";
|
|
53
|
+
const examples = localScope ? scopedSuggestions : estateSuggestions;
|
|
54
|
+
const visibleAnswer = answerForState(state);
|
|
55
|
+
const previousAnswer = state.status === "loading" || state.status === "error" ? state.previous : void 0;
|
|
56
|
+
const submitQuestion = async (explicitQuestion = question) => {
|
|
57
|
+
if (activeRequest.current?.requestContext === requestContext) return;
|
|
58
|
+
const clientError = validateQuestion(explicitQuestion);
|
|
59
|
+
if (clientError) {
|
|
60
|
+
setValidationError(clientError);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
setValidationError(void 0);
|
|
64
|
+
const generation = requestGeneration.current + 1;
|
|
65
|
+
requestGeneration.current = generation;
|
|
66
|
+
activeRequest.current = { generation, requestContext };
|
|
67
|
+
const previous = visibleAnswer;
|
|
68
|
+
setState({ status: "loading", previous });
|
|
69
|
+
try {
|
|
70
|
+
const request = localScope ? { question: explicitQuestion, scope: localScope } : { question: explicitQuestion };
|
|
71
|
+
const answer = await api.ask(request);
|
|
72
|
+
if (ownsActiveRequest(activeRequest.current, generation, requestContext)) {
|
|
73
|
+
setState({ status: "success", answer });
|
|
74
|
+
}
|
|
75
|
+
} catch (error) {
|
|
76
|
+
if (ownsActiveRequest(activeRequest.current, generation, requestContext)) {
|
|
77
|
+
setState({
|
|
78
|
+
status: "error",
|
|
79
|
+
error: safeRequestError(error),
|
|
80
|
+
previous
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
} finally {
|
|
84
|
+
if (ownsActiveRequest(activeRequest.current, generation, requestContext)) {
|
|
85
|
+
activeRequest.current = void 0;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
const submitExplicitQuestion = (nextQuestion) => {
|
|
90
|
+
if (activeRequest.current?.requestContext === requestContext) return;
|
|
91
|
+
setQuestion(nextQuestion);
|
|
92
|
+
void submitQuestion(nextQuestion);
|
|
93
|
+
};
|
|
94
|
+
const onSubmit = (event) => {
|
|
95
|
+
event.preventDefault();
|
|
96
|
+
void submitQuestion();
|
|
97
|
+
};
|
|
98
|
+
const onQuestionKeyDown = (event) => {
|
|
99
|
+
if (event.key !== "Enter" || !event.metaKey && !event.ctrlKey) return;
|
|
100
|
+
event.preventDefault();
|
|
101
|
+
void submitQuestion();
|
|
102
|
+
};
|
|
103
|
+
return /* @__PURE__ */ jsx(Card, { variant: "outlined", children: /* @__PURE__ */ jsxs(CardContent, { children: [
|
|
104
|
+
/* @__PURE__ */ jsx(Typography, { component: "h2", variant: "h6", gutterBottom: true, children: "Ask Anyshift" }),
|
|
105
|
+
context === "entity" && /* @__PURE__ */ jsx(
|
|
106
|
+
Box,
|
|
107
|
+
{
|
|
108
|
+
mb: 2,
|
|
109
|
+
display: "flex",
|
|
110
|
+
alignItems: "center",
|
|
111
|
+
flexWrap: "wrap",
|
|
112
|
+
gridGap: 8,
|
|
113
|
+
children: localScope ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
114
|
+
/* @__PURE__ */ jsx(
|
|
115
|
+
Chip,
|
|
116
|
+
{
|
|
117
|
+
size: "small",
|
|
118
|
+
color: "primary",
|
|
119
|
+
variant: "outlined",
|
|
120
|
+
label: `Scope \xB7 ${localScope.target}`
|
|
121
|
+
}
|
|
122
|
+
),
|
|
123
|
+
/* @__PURE__ */ jsx(
|
|
124
|
+
Button,
|
|
125
|
+
{
|
|
126
|
+
type: "button",
|
|
127
|
+
size: "small",
|
|
128
|
+
disabled: loading,
|
|
129
|
+
onClick: () => setScopeRemoved(true),
|
|
130
|
+
"aria-label": `Remove ${localScope.target} scope`,
|
|
131
|
+
children: "Remove"
|
|
132
|
+
}
|
|
133
|
+
)
|
|
134
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
135
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", color: "textSecondary", children: "Searching the whole connected project" }),
|
|
136
|
+
restorableScope && /* @__PURE__ */ jsx(
|
|
137
|
+
Button,
|
|
138
|
+
{
|
|
139
|
+
type: "button",
|
|
140
|
+
size: "small",
|
|
141
|
+
disabled: loading,
|
|
142
|
+
onClick: () => setScopeRemoved(false),
|
|
143
|
+
"aria-label": `Restore ${restorableScope.target} scope`,
|
|
144
|
+
children: "Restore scope"
|
|
145
|
+
}
|
|
146
|
+
)
|
|
147
|
+
] })
|
|
148
|
+
}
|
|
149
|
+
),
|
|
150
|
+
/* @__PURE__ */ jsxs("form", { onSubmit, noValidate: true, children: [
|
|
151
|
+
/* @__PURE__ */ jsx(
|
|
152
|
+
TextField,
|
|
153
|
+
{
|
|
154
|
+
id: "ask-anyshift-question",
|
|
155
|
+
label: "Question",
|
|
156
|
+
value: question,
|
|
157
|
+
onChange: (event) => {
|
|
158
|
+
setQuestion(event.target.value);
|
|
159
|
+
if (validationError) setValidationError(void 0);
|
|
160
|
+
},
|
|
161
|
+
onKeyDown: onQuestionKeyDown,
|
|
162
|
+
disabled: loading,
|
|
163
|
+
error: Boolean(validationError),
|
|
164
|
+
helperText: validationError ?? IDLE_HELPER_TEXT,
|
|
165
|
+
multiline: true,
|
|
166
|
+
minRows: 2,
|
|
167
|
+
fullWidth: true,
|
|
168
|
+
variant: "outlined"
|
|
169
|
+
}
|
|
170
|
+
),
|
|
171
|
+
/* @__PURE__ */ jsx(Box, { mt: 1, display: "flex", justifyContent: "flex-end", children: /* @__PURE__ */ jsx(
|
|
172
|
+
Button,
|
|
173
|
+
{
|
|
174
|
+
type: "submit",
|
|
175
|
+
color: "primary",
|
|
176
|
+
variant: "contained",
|
|
177
|
+
disabled: loading,
|
|
178
|
+
startIcon: loading ? /* @__PURE__ */ jsx(CircularProgress, { size: 16 }) : void 0,
|
|
179
|
+
children: loading ? "Asking\u2026" : "Ask"
|
|
180
|
+
}
|
|
181
|
+
) })
|
|
182
|
+
] }),
|
|
183
|
+
/* @__PURE__ */ jsx(
|
|
184
|
+
QuestionChoices,
|
|
185
|
+
{
|
|
186
|
+
title: "Try an example",
|
|
187
|
+
questions: examples,
|
|
188
|
+
disabled: loading,
|
|
189
|
+
onQuestion: submitExplicitQuestion
|
|
190
|
+
}
|
|
191
|
+
),
|
|
192
|
+
state.status === "error" && /* @__PURE__ */ jsxs(Box, { mt: 2, role: "alert", "aria-live": "polite", children: [
|
|
193
|
+
/* @__PURE__ */ jsx(Typography, { color: "error", children: errorCopy(state.error) }),
|
|
194
|
+
isRetryable(state.error) && /* @__PURE__ */ jsx(Box, { mt: 1, children: /* @__PURE__ */ jsx(
|
|
195
|
+
Button,
|
|
196
|
+
{
|
|
197
|
+
type: "button",
|
|
198
|
+
size: "small",
|
|
199
|
+
variant: "outlined",
|
|
200
|
+
onClick: () => void submitQuestion(),
|
|
201
|
+
children: "Retry"
|
|
202
|
+
}
|
|
203
|
+
) })
|
|
204
|
+
] }),
|
|
205
|
+
loading && /* @__PURE__ */ jsx(Typography, { role: "status", variant: "body2", color: "textSecondary", children: "Searching the connected graph\u2026" }),
|
|
206
|
+
visibleAnswer && /* @__PURE__ */ jsxs(Box, { mt: 3, children: [
|
|
207
|
+
previousAnswer && /* @__PURE__ */ jsx(Typography, { component: "h3", variant: "subtitle2", gutterBottom: true, children: "Previous answer" }),
|
|
208
|
+
/* @__PURE__ */ jsx(
|
|
209
|
+
SafeAnswer,
|
|
210
|
+
{
|
|
211
|
+
answer: visibleAnswer,
|
|
212
|
+
onQuestion: loading ? void 0 : submitExplicitQuestion
|
|
213
|
+
}
|
|
214
|
+
),
|
|
215
|
+
!loading && /* @__PURE__ */ jsx(
|
|
216
|
+
QuestionChoices,
|
|
217
|
+
{
|
|
218
|
+
title: "Ask another question",
|
|
219
|
+
questions: followUpsForIntent(
|
|
220
|
+
visibleAnswer.result.intent,
|
|
221
|
+
Boolean(localScope)
|
|
222
|
+
),
|
|
223
|
+
disabled: false,
|
|
224
|
+
onQuestion: submitExplicitQuestion
|
|
225
|
+
}
|
|
226
|
+
)
|
|
227
|
+
] })
|
|
228
|
+
] }) });
|
|
229
|
+
};
|
|
230
|
+
const QuestionChoices = ({
|
|
231
|
+
title,
|
|
232
|
+
questions,
|
|
233
|
+
disabled,
|
|
234
|
+
onQuestion
|
|
235
|
+
}) => /* @__PURE__ */ jsxs(Box, { mt: 2, children: [
|
|
236
|
+
/* @__PURE__ */ jsx(Typography, { variant: "subtitle2", gutterBottom: true, children: title }),
|
|
237
|
+
/* @__PURE__ */ jsx(Box, { display: "flex", flexWrap: "wrap", gridGap: 8, children: questions.map((suggestion) => /* @__PURE__ */ jsx(
|
|
238
|
+
Button,
|
|
239
|
+
{
|
|
240
|
+
type: "button",
|
|
241
|
+
size: "small",
|
|
242
|
+
variant: "outlined",
|
|
243
|
+
disabled,
|
|
244
|
+
onClick: () => onQuestion(suggestion),
|
|
245
|
+
children: suggestion
|
|
246
|
+
},
|
|
247
|
+
suggestion
|
|
248
|
+
)) })
|
|
249
|
+
] });
|
|
250
|
+
const SafeAnswer = ({
|
|
251
|
+
answer,
|
|
252
|
+
onQuestion
|
|
253
|
+
}) => {
|
|
254
|
+
try {
|
|
255
|
+
presentAskResult(answer.result);
|
|
256
|
+
} catch {
|
|
257
|
+
return /* @__PURE__ */ jsxs(Box, { "aria-label": "Anyshift answer", children: [
|
|
258
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body1", children: answer.result.summary }),
|
|
259
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", color: "textSecondary", children: "Detailed evidence is not supported by this Backstage version." })
|
|
260
|
+
] });
|
|
261
|
+
}
|
|
262
|
+
return /* @__PURE__ */ jsx(
|
|
263
|
+
AskResultPresenter,
|
|
264
|
+
{
|
|
265
|
+
result: answer.result,
|
|
266
|
+
effectiveQuestion: answer.effectiveQuestion,
|
|
267
|
+
answeredAt: answer.answeredAt,
|
|
268
|
+
onQuestion
|
|
269
|
+
}
|
|
270
|
+
);
|
|
271
|
+
};
|
|
272
|
+
function answerForState(state) {
|
|
273
|
+
switch (state.status) {
|
|
274
|
+
case "success":
|
|
275
|
+
return state.answer;
|
|
276
|
+
case "loading":
|
|
277
|
+
case "error":
|
|
278
|
+
return state.previous;
|
|
279
|
+
case "idle":
|
|
280
|
+
return void 0;
|
|
281
|
+
default:
|
|
282
|
+
return assertNever(state);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
function ownsActiveRequest(activeRequest, generation, requestContext) {
|
|
286
|
+
return activeRequest?.generation === generation && activeRequest.requestContext === requestContext;
|
|
287
|
+
}
|
|
288
|
+
function requestContextKey(context, entityRef, scope) {
|
|
289
|
+
if (context === "estate") return "estate";
|
|
290
|
+
const identity = entityRef?.trim() ?? "";
|
|
291
|
+
return scope ? `entity:${identity}:target:${scope.target.trim()}` : `entity:${identity}:unscoped`;
|
|
292
|
+
}
|
|
293
|
+
function validateQuestion(value) {
|
|
294
|
+
if (!value.trim()) return "Enter a question.";
|
|
295
|
+
if (Array.from(value).length > MAX_QUESTION_CODE_POINTS) {
|
|
296
|
+
return "Questions must be 500 characters or fewer.";
|
|
297
|
+
}
|
|
298
|
+
return void 0;
|
|
299
|
+
}
|
|
300
|
+
function isValidScope(scope) {
|
|
301
|
+
if (!scope?.entityRef.trim() || !scope.target.trim()) return false;
|
|
302
|
+
if (Array.from(scope.target).length > MAX_SCOPE_TARGET_CODE_POINTS) {
|
|
303
|
+
return false;
|
|
304
|
+
}
|
|
305
|
+
return !Array.from(scope.target).some((character) => {
|
|
306
|
+
const codePoint = character.codePointAt(0);
|
|
307
|
+
return codePoint !== void 0 && (codePoint <= 31 || codePoint >= 127 && codePoint <= 159);
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
function safeRequestError(error) {
|
|
311
|
+
return error instanceof AnyshiftRequestError ? error : new AnyshiftRequestError(0, "unavailable", "Unavailable");
|
|
312
|
+
}
|
|
313
|
+
function errorCopy(error) {
|
|
314
|
+
if (error.status === 400 || error.code === "bad_request") {
|
|
315
|
+
return "Ask Anyshift could not answer that question. Try rephrasing it or choose an example.";
|
|
316
|
+
}
|
|
317
|
+
if (error.status === 403 || error.code === "forbidden") {
|
|
318
|
+
return "Ask Anyshift access is unavailable for your account.";
|
|
319
|
+
}
|
|
320
|
+
if (error.status === 413 || error.code === "payload_too_large") {
|
|
321
|
+
return "Questions must be 500 characters or fewer.";
|
|
322
|
+
}
|
|
323
|
+
if (error.status === 429 || error.code === "rate_limited") {
|
|
324
|
+
return typeof error.retryAfterSeconds === "number" ? `Ask Anyshift is temporarily rate limited. Try again in ${error.retryAfterSeconds} seconds.` : "Ask Anyshift is temporarily rate limited. Try again shortly.";
|
|
325
|
+
}
|
|
326
|
+
if (error.status === 504 || error.code === "timeout") {
|
|
327
|
+
return "The graph question timed out. Try again.";
|
|
328
|
+
}
|
|
329
|
+
if (error.status === 0 || error.status === 502 || error.status === 503 || error.code === "unavailable" || error.code === "upstream_unavailable" || error.code === "request_failed") {
|
|
330
|
+
return "Ask Anyshift is temporarily unavailable. Try again.";
|
|
331
|
+
}
|
|
332
|
+
return "Ask Anyshift could not complete the question. Try again.";
|
|
333
|
+
}
|
|
334
|
+
function isRetryable(error) {
|
|
335
|
+
return error.status === 0 || error.status === 429 || error.status === 502 || error.status === 503 || error.status === 504 || error.code === "timeout" || error.code === "unavailable" || error.code === "upstream_unavailable" || error.code === "request_failed";
|
|
336
|
+
}
|
|
337
|
+
function assertNever(value) {
|
|
338
|
+
throw new Error(`Unsupported Ask card state: ${String(value)}`);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
export { AskAnyshiftCard };
|
|
342
|
+
//# sourceMappingURL=AskAnyshiftCard.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AskAnyshiftCard.esm.js","sources":["../../../src/components/ask/AskAnyshiftCard.tsx"],"sourcesContent":["import {\n Box,\n Button,\n Card,\n CardContent,\n Chip,\n CircularProgress,\n TextField,\n Typography,\n} from '@material-ui/core';\nimport { useApi } from '@backstage/frontend-plugin-api';\nimport {\n FormEvent,\n KeyboardEvent,\n useLayoutEffect,\n useRef,\n useState,\n} from 'react';\nimport { anyshiftApiRef, AnyshiftRequestError } from '../../api/AnyshiftApi';\nimport type { AskAnyshiftResponse, AskScope } from '../../api/types';\nimport {\n estateSuggestions,\n followUpsForIntent,\n scopedSuggestions,\n} from './askSuggestions';\nimport { AskResultPresenter, presentAskResult } from './askPresenters';\n\nconst MAX_QUESTION_CODE_POINTS = 500;\nconst MAX_SCOPE_TARGET_CODE_POINTS = 200;\nconst IDLE_HELPER_TEXT =\n 'Read-only answers from your connected graph. Do not include secrets.';\n\ntype AskCardState =\n | { status: 'idle' }\n | { status: 'loading'; previous?: AskAnyshiftResponse }\n | { status: 'success'; answer: AskAnyshiftResponse }\n | {\n status: 'error';\n error: AnyshiftRequestError;\n previous?: AskAnyshiftResponse;\n };\n\nexport interface AskAnyshiftCardProps {\n context: 'estate' | 'entity';\n scope?: AskScope;\n}\n\nexport const AskAnyshiftCard = ({ context, scope }: AskAnyshiftCardProps) => {\n const api = useApi(anyshiftApiRef);\n const validPropScope =\n context === 'entity' && isValidScope(scope) ? scope : undefined;\n const propScopeIdentity =\n context === 'entity' && scope?.entityRef.trim()\n ? scope.entityRef\n : undefined;\n const mountedIdentity =\n context === 'estate' ? 'estate' : `entity:${propScopeIdentity ?? ''}`;\n const previousMountedIdentity = useRef(mountedIdentity);\n const requestGeneration = useRef(0);\n const activeRequest = useRef<{\n generation: number;\n requestContext: string;\n }>();\n const [question, setQuestion] = useState('');\n const [validationError, setValidationError] = useState<string>();\n const [state, setState] = useState<AskCardState>({ status: 'idle' });\n const [scopeRemoved, setScopeRemoved] = useState(false);\n const localScope = scopeRemoved ? undefined : validPropScope;\n const restorableScope = validPropScope;\n const requestContext = requestContextKey(\n context,\n propScopeIdentity,\n localScope,\n );\n const previousRequestContext = useRef(requestContext);\n\n useLayoutEffect(() => {\n if (previousMountedIdentity.current === mountedIdentity) return;\n previousMountedIdentity.current = mountedIdentity;\n requestGeneration.current += 1;\n activeRequest.current = undefined;\n setState({ status: 'idle' });\n setValidationError(undefined);\n setScopeRemoved(false);\n }, [mountedIdentity]);\n\n useLayoutEffect(() => {\n if (previousRequestContext.current === requestContext) return;\n previousRequestContext.current = requestContext;\n requestGeneration.current += 1;\n if (!activeRequest.current) return;\n activeRequest.current = undefined;\n setState(current =>\n current.status === 'loading' ? { status: 'idle' } : current,\n );\n setValidationError(undefined);\n }, [requestContext]);\n\n const loading = state.status === 'loading';\n const examples = localScope ? scopedSuggestions : estateSuggestions;\n const visibleAnswer = answerForState(state);\n const previousAnswer =\n state.status === 'loading' || state.status === 'error'\n ? state.previous\n : undefined;\n\n const submitQuestion = async (explicitQuestion = question) => {\n if (activeRequest.current?.requestContext === requestContext) return;\n const clientError = validateQuestion(explicitQuestion);\n if (clientError) {\n setValidationError(clientError);\n return;\n }\n\n setValidationError(undefined);\n const generation = requestGeneration.current + 1;\n requestGeneration.current = generation;\n activeRequest.current = { generation, requestContext };\n const previous = visibleAnswer;\n setState({ status: 'loading', previous });\n try {\n const request = localScope\n ? { question: explicitQuestion, scope: localScope }\n : { question: explicitQuestion };\n const answer = await api.ask(request);\n if (\n ownsActiveRequest(activeRequest.current, generation, requestContext)\n ) {\n setState({ status: 'success', answer });\n }\n } catch (error) {\n if (\n ownsActiveRequest(activeRequest.current, generation, requestContext)\n ) {\n setState({\n status: 'error',\n error: safeRequestError(error),\n previous,\n });\n }\n } finally {\n if (\n ownsActiveRequest(activeRequest.current, generation, requestContext)\n ) {\n activeRequest.current = undefined;\n }\n }\n };\n\n const submitExplicitQuestion = (nextQuestion: string) => {\n if (activeRequest.current?.requestContext === requestContext) return;\n setQuestion(nextQuestion);\n void submitQuestion(nextQuestion);\n };\n\n const onSubmit = (event: FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n void submitQuestion();\n };\n\n const onQuestionKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n if (event.key !== 'Enter' || (!event.metaKey && !event.ctrlKey)) return;\n event.preventDefault();\n void submitQuestion();\n };\n\n return (\n <Card variant=\"outlined\">\n <CardContent>\n <Typography component=\"h2\" variant=\"h6\" gutterBottom>\n Ask Anyshift\n </Typography>\n\n {context === 'entity' && (\n <Box\n mb={2}\n display=\"flex\"\n alignItems=\"center\"\n flexWrap=\"wrap\"\n gridGap={8}\n >\n {localScope ? (\n <>\n <Chip\n size=\"small\"\n color=\"primary\"\n variant=\"outlined\"\n label={`Scope · ${localScope.target}`}\n />\n <Button\n type=\"button\"\n size=\"small\"\n disabled={loading}\n onClick={() => setScopeRemoved(true)}\n aria-label={`Remove ${localScope.target} scope`}\n >\n Remove\n </Button>\n </>\n ) : (\n <>\n <Typography variant=\"body2\" color=\"textSecondary\">\n Searching the whole connected project\n </Typography>\n {restorableScope && (\n <Button\n type=\"button\"\n size=\"small\"\n disabled={loading}\n onClick={() => setScopeRemoved(false)}\n aria-label={`Restore ${restorableScope.target} scope`}\n >\n Restore scope\n </Button>\n )}\n </>\n )}\n </Box>\n )}\n\n <form onSubmit={onSubmit} noValidate>\n <TextField\n id=\"ask-anyshift-question\"\n label=\"Question\"\n value={question}\n onChange={event => {\n setQuestion(event.target.value);\n if (validationError) setValidationError(undefined);\n }}\n onKeyDown={onQuestionKeyDown}\n disabled={loading}\n error={Boolean(validationError)}\n helperText={validationError ?? IDLE_HELPER_TEXT}\n multiline\n minRows={2}\n fullWidth\n variant=\"outlined\"\n />\n <Box mt={1} display=\"flex\" justifyContent=\"flex-end\">\n <Button\n type=\"submit\"\n color=\"primary\"\n variant=\"contained\"\n disabled={loading}\n startIcon={loading ? <CircularProgress size={16} /> : undefined}\n >\n {loading ? 'Asking…' : 'Ask'}\n </Button>\n </Box>\n </form>\n\n <QuestionChoices\n title=\"Try an example\"\n questions={examples}\n disabled={loading}\n onQuestion={submitExplicitQuestion}\n />\n\n {state.status === 'error' && (\n <Box mt={2} role=\"alert\" aria-live=\"polite\">\n <Typography color=\"error\">{errorCopy(state.error)}</Typography>\n {isRetryable(state.error) && (\n <Box mt={1}>\n <Button\n type=\"button\"\n size=\"small\"\n variant=\"outlined\"\n onClick={() => void submitQuestion()}\n >\n Retry\n </Button>\n </Box>\n )}\n </Box>\n )}\n\n {loading && (\n <Typography role=\"status\" variant=\"body2\" color=\"textSecondary\">\n Searching the connected graph…\n </Typography>\n )}\n\n {visibleAnswer && (\n <Box mt={3}>\n {previousAnswer && (\n <Typography component=\"h3\" variant=\"subtitle2\" gutterBottom>\n Previous answer\n </Typography>\n )}\n <SafeAnswer\n answer={visibleAnswer}\n onQuestion={loading ? undefined : submitExplicitQuestion}\n />\n {!loading && (\n <QuestionChoices\n title=\"Ask another question\"\n questions={followUpsForIntent(\n visibleAnswer.result.intent,\n Boolean(localScope),\n )}\n disabled={false}\n onQuestion={submitExplicitQuestion}\n />\n )}\n </Box>\n )}\n </CardContent>\n </Card>\n );\n};\n\nconst QuestionChoices = ({\n title,\n questions,\n disabled,\n onQuestion,\n}: {\n title: string;\n questions: readonly string[];\n disabled: boolean;\n onQuestion: (question: string) => void;\n}) => (\n <Box mt={2}>\n <Typography variant=\"subtitle2\" gutterBottom>\n {title}\n </Typography>\n <Box display=\"flex\" flexWrap=\"wrap\" gridGap={8}>\n {questions.map(suggestion => (\n <Button\n key={suggestion}\n type=\"button\"\n size=\"small\"\n variant=\"outlined\"\n disabled={disabled}\n onClick={() => onQuestion(suggestion)}\n >\n {suggestion}\n </Button>\n ))}\n </Box>\n </Box>\n);\n\nconst SafeAnswer = ({\n answer,\n onQuestion,\n}: {\n answer: AskAnyshiftResponse;\n onQuestion?: (question: string) => void;\n}) => {\n try {\n presentAskResult(answer.result);\n } catch {\n return (\n <Box aria-label=\"Anyshift answer\">\n <Typography variant=\"body1\">{answer.result.summary}</Typography>\n <Typography variant=\"body2\" color=\"textSecondary\">\n Detailed evidence is not supported by this Backstage version.\n </Typography>\n </Box>\n );\n }\n\n return (\n <AskResultPresenter\n result={answer.result}\n effectiveQuestion={answer.effectiveQuestion}\n answeredAt={answer.answeredAt}\n onQuestion={onQuestion}\n />\n );\n};\n\nfunction answerForState(state: AskCardState): AskAnyshiftResponse | undefined {\n switch (state.status) {\n case 'success':\n return state.answer;\n case 'loading':\n case 'error':\n return state.previous;\n case 'idle':\n return undefined;\n default:\n return assertNever(state);\n }\n}\n\nfunction ownsActiveRequest(\n activeRequest: { generation: number; requestContext: string } | undefined,\n generation: number,\n requestContext: string,\n): boolean {\n return (\n activeRequest?.generation === generation &&\n activeRequest.requestContext === requestContext\n );\n}\n\nfunction requestContextKey(\n context: 'estate' | 'entity',\n entityRef: string | undefined,\n scope: AskScope | undefined,\n): string {\n if (context === 'estate') return 'estate';\n const identity = entityRef?.trim() ?? '';\n return scope\n ? `entity:${identity}:target:${scope.target.trim()}`\n : `entity:${identity}:unscoped`;\n}\n\nfunction validateQuestion(value: string): string | undefined {\n if (!value.trim()) return 'Enter a question.';\n if (Array.from(value).length > MAX_QUESTION_CODE_POINTS) {\n return 'Questions must be 500 characters or fewer.';\n }\n return undefined;\n}\n\nfunction isValidScope(scope: AskScope | undefined): scope is AskScope {\n if (!scope?.entityRef.trim() || !scope.target.trim()) return false;\n if (Array.from(scope.target).length > MAX_SCOPE_TARGET_CODE_POINTS) {\n return false;\n }\n return !Array.from(scope.target).some(character => {\n const codePoint = character.codePointAt(0);\n return (\n codePoint !== undefined &&\n (codePoint <= 0x1f || (codePoint >= 0x7f && codePoint <= 0x9f))\n );\n });\n}\n\nfunction safeRequestError(error: unknown): AnyshiftRequestError {\n return error instanceof AnyshiftRequestError\n ? error\n : new AnyshiftRequestError(0, 'unavailable', 'Unavailable');\n}\n\nfunction errorCopy(error: AnyshiftRequestError): string {\n if (error.status === 400 || error.code === 'bad_request') {\n return 'Ask Anyshift could not answer that question. Try rephrasing it or choose an example.';\n }\n if (error.status === 403 || error.code === 'forbidden') {\n return 'Ask Anyshift access is unavailable for your account.';\n }\n if (error.status === 413 || error.code === 'payload_too_large') {\n return 'Questions must be 500 characters or fewer.';\n }\n if (error.status === 429 || error.code === 'rate_limited') {\n return typeof error.retryAfterSeconds === 'number'\n ? `Ask Anyshift is temporarily rate limited. Try again in ${error.retryAfterSeconds} seconds.`\n : 'Ask Anyshift is temporarily rate limited. Try again shortly.';\n }\n if (error.status === 504 || error.code === 'timeout') {\n return 'The graph question timed out. Try again.';\n }\n if (\n error.status === 0 ||\n error.status === 502 ||\n error.status === 503 ||\n error.code === 'unavailable' ||\n error.code === 'upstream_unavailable' ||\n error.code === 'request_failed'\n ) {\n return 'Ask Anyshift is temporarily unavailable. Try again.';\n }\n return 'Ask Anyshift could not complete the question. Try again.';\n}\n\nfunction isRetryable(error: AnyshiftRequestError): boolean {\n return (\n error.status === 0 ||\n error.status === 429 ||\n error.status === 502 ||\n error.status === 503 ||\n error.status === 504 ||\n error.code === 'timeout' ||\n error.code === 'unavailable' ||\n error.code === 'upstream_unavailable' ||\n error.code === 'request_failed'\n );\n}\n\nfunction assertNever(value: never): never {\n throw new Error(`Unsupported Ask card state: ${String(value)}`);\n}\n"],"names":[],"mappings":"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"}
|