@arizeai/phoenix-cli 1.8.1 → 1.9.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/README.md +88 -7
- package/build/authFetch.d.ts +24 -0
- package/build/authFetch.d.ts.map +1 -0
- package/build/authFetch.js +44 -0
- package/build/authFetch.js.map +1 -0
- package/build/cli.d.ts.map +1 -1
- package/build/cli.js +2 -1
- package/build/cli.js.map +1 -1
- package/build/client.d.ts +5 -1
- package/build/client.d.ts.map +1 -1
- package/build/client.js +6 -1
- package/build/client.js.map +1 -1
- package/build/commands/annotationConfig.d.ts.map +1 -1
- package/build/commands/annotationConfig.js.map +1 -1
- package/build/commands/{annotationMutationUtils.d.ts → annotationMutations.d.ts} +1 -1
- package/build/commands/annotationMutations.d.ts.map +1 -0
- package/build/commands/{annotationMutationUtils.js → annotationMutations.js} +1 -1
- package/build/commands/annotationMutations.js.map +1 -0
- package/build/commands/api.d.ts.map +1 -1
- package/build/commands/api.js +12 -1
- package/build/commands/api.js.map +1 -1
- package/build/commands/auth.d.ts +5 -2
- package/build/commands/auth.d.ts.map +1 -1
- package/build/commands/auth.js +332 -25
- package/build/commands/auth.js.map +1 -1
- package/build/commands/dataset.d.ts.map +1 -1
- package/build/commands/dataset.js.map +1 -1
- package/build/commands/docs.d.ts +51 -0
- package/build/commands/docs.d.ts.map +1 -1
- package/build/commands/docs.js +86 -37
- package/build/commands/docs.js.map +1 -1
- package/build/commands/experiment.d.ts.map +1 -1
- package/build/commands/experiment.js.map +1 -1
- package/build/commands/fetchSpanAnnotations.d.ts +14 -0
- package/build/commands/fetchSpanAnnotations.d.ts.map +1 -0
- package/build/commands/fetchSpanAnnotations.js +57 -0
- package/build/commands/fetchSpanAnnotations.js.map +1 -0
- package/build/commands/fetchTraceAnnotations.d.ts +14 -0
- package/build/commands/fetchTraceAnnotations.d.ts.map +1 -0
- package/build/commands/fetchTraceAnnotations.js +57 -0
- package/build/commands/fetchTraceAnnotations.js.map +1 -0
- package/build/commands/formatAnnotationMutation.d.ts +1 -1
- package/build/commands/formatAnnotationMutation.d.ts.map +1 -1
- package/build/commands/formatNoteMutation.d.ts +1 -1
- package/build/commands/formatNoteMutation.d.ts.map +1 -1
- package/build/commands/formatSetup.d.ts +56 -0
- package/build/commands/formatSetup.d.ts.map +1 -0
- package/build/commands/formatSetup.js +73 -0
- package/build/commands/formatSetup.js.map +1 -0
- package/build/commands/index.d.ts +4 -3
- package/build/commands/index.d.ts.map +1 -1
- package/build/commands/index.js +4 -3
- package/build/commands/index.js.map +1 -1
- package/build/commands/{noteMutationUtils.d.ts → noteMutations.d.ts} +1 -1
- package/build/commands/noteMutations.d.ts.map +1 -0
- package/build/commands/{noteMutationUtils.js → noteMutations.js} +1 -1
- package/build/commands/noteMutations.js.map +1 -0
- package/build/commands/options.d.ts +248 -0
- package/build/commands/options.d.ts.map +1 -0
- package/build/commands/options.js +16 -0
- package/build/commands/options.js.map +1 -0
- package/build/commands/profile.d.ts.map +1 -1
- package/build/commands/profile.js +3 -11
- package/build/commands/profile.js.map +1 -1
- package/build/commands/project.d.ts.map +1 -1
- package/build/commands/project.js +0 -1
- package/build/commands/project.js.map +1 -1
- package/build/commands/prompt.d.ts.map +1 -1
- package/build/commands/prompt.js.map +1 -1
- package/build/commands/self.d.ts.map +1 -1
- package/build/commands/self.js.map +1 -1
- package/build/commands/session.d.ts.map +1 -1
- package/build/commands/session.js +4 -4
- package/build/commands/session.js.map +1 -1
- package/build/commands/{sessionAnnotationsCommand.d.ts → sessionAnnotations.d.ts} +1 -1
- package/build/commands/sessionAnnotations.d.ts.map +1 -0
- package/build/commands/{sessionAnnotationsCommand.js → sessionAnnotations.js} +1 -1
- package/build/commands/sessionAnnotations.js.map +1 -0
- package/build/commands/setup.d.ts +19 -0
- package/build/commands/setup.d.ts.map +1 -0
- package/build/commands/setup.js +249 -0
- package/build/commands/setup.js.map +1 -0
- package/build/commands/span.d.ts.map +1 -1
- package/build/commands/span.js +5 -5
- package/build/commands/span.js.map +1 -1
- package/build/commands/spanAnnotations.d.ts +6 -13
- package/build/commands/spanAnnotations.d.ts.map +1 -1
- package/build/commands/spanAnnotations.js +154 -47
- package/build/commands/spanAnnotations.js.map +1 -1
- package/build/commands/trace.d.ts.map +1 -1
- package/build/commands/trace.js +6 -6
- package/build/commands/trace.js.map +1 -1
- package/build/commands/traceAnnotations.d.ts +6 -13
- package/build/commands/traceAnnotations.d.ts.map +1 -1
- package/build/commands/traceAnnotations.js +157 -47
- package/build/commands/traceAnnotations.js.map +1 -1
- package/build/config.d.ts +15 -0
- package/build/config.d.ts.map +1 -1
- package/build/config.js +54 -7
- package/build/config.js.map +1 -1
- package/build/curl.d.ts +17 -0
- package/build/curl.d.ts.map +1 -1
- package/build/curl.js.map +1 -1
- package/build/exitCodes.d.ts +6 -0
- package/build/exitCodes.d.ts.map +1 -1
- package/build/exitCodes.js +18 -0
- package/build/exitCodes.js.map +1 -1
- package/build/index.js +2 -1
- package/build/index.js.map +1 -1
- package/build/oauth.d.ts +138 -0
- package/build/oauth.d.ts.map +1 -0
- package/build/oauth.js +465 -0
- package/build/oauth.js.map +1 -0
- package/build/oauthCallbackPage.d.ts +9 -0
- package/build/oauthCallbackPage.d.ts.map +1 -0
- package/build/oauthCallbackPage.js +111 -0
- package/build/oauthCallbackPage.js.map +1 -0
- package/build/optionParsers.d.ts +12 -0
- package/build/optionParsers.d.ts.map +1 -1
- package/build/optionParsers.js +15 -0
- package/build/optionParsers.js.map +1 -1
- package/build/pxi/App.d.ts.map +1 -1
- package/build/pxi/App.js +4 -0
- package/build/pxi/App.js.map +1 -1
- package/build/pxi/client.d.ts.map +1 -1
- package/build/pxi/client.js +11 -2
- package/build/pxi/client.js.map +1 -1
- package/build/pxi/options.d.ts +98 -0
- package/build/pxi/options.d.ts.map +1 -1
- package/build/pxi/options.js.map +1 -1
- package/build/settings.d.ts +19 -0
- package/build/settings.d.ts.map +1 -1
- package/build/settings.js +25 -0
- package/build/settings.js.map +1 -1
- package/build/setup/agents/registry.d.ts +135 -0
- package/build/setup/agents/registry.d.ts.map +1 -0
- package/build/setup/agents/registry.js +174 -0
- package/build/setup/agents/registry.js.map +1 -0
- package/build/setup/copy.d.ts +199 -0
- package/build/setup/copy.d.ts.map +1 -0
- package/build/setup/copy.js +334 -0
- package/build/setup/copy.js.map +1 -0
- package/build/setup/deps/buildDefaultDeps.d.ts +13 -0
- package/build/setup/deps/buildDefaultDeps.d.ts.map +1 -0
- package/build/setup/deps/buildDefaultDeps.js +31 -0
- package/build/setup/deps/buildDefaultDeps.js.map +1 -0
- package/build/setup/deps/clipboard.d.ts +12 -0
- package/build/setup/deps/clipboard.d.ts.map +1 -0
- package/build/setup/deps/clipboard.js +30 -0
- package/build/setup/deps/clipboard.js.map +1 -0
- package/build/setup/deps/clock.d.ts +10 -0
- package/build/setup/deps/clock.d.ts.map +1 -0
- package/build/setup/deps/clock.js +11 -0
- package/build/setup/deps/clock.js.map +1 -0
- package/build/setup/deps/context.d.ts +28 -0
- package/build/setup/deps/context.d.ts.map +1 -0
- package/build/setup/deps/context.js +24 -0
- package/build/setup/deps/context.js.map +1 -0
- package/build/setup/deps/docs.d.ts +41 -0
- package/build/setup/deps/docs.d.ts.map +1 -0
- package/build/setup/deps/docs.js +33 -0
- package/build/setup/deps/docs.js.map +1 -0
- package/build/setup/deps/index.d.ts +46 -0
- package/build/setup/deps/index.d.ts.map +1 -0
- package/build/setup/deps/index.js +16 -0
- package/build/setup/deps/index.js.map +1 -0
- package/build/setup/deps/oauthLogin.d.ts +64 -0
- package/build/setup/deps/oauthLogin.d.ts.map +1 -0
- package/build/setup/deps/oauthLogin.js +82 -0
- package/build/setup/deps/oauthLogin.js.map +1 -0
- package/build/setup/deps/phoenixClient.d.ts +22 -0
- package/build/setup/deps/phoenixClient.d.ts.map +1 -0
- package/build/setup/deps/phoenixClient.js +17 -0
- package/build/setup/deps/phoenixClient.js.map +1 -0
- package/build/setup/deps/processes.d.ts +28 -0
- package/build/setup/deps/processes.d.ts.map +1 -0
- package/build/setup/deps/processes.js +58 -0
- package/build/setup/deps/processes.js.map +1 -0
- package/build/setup/deps/prompter.d.ts +49 -0
- package/build/setup/deps/prompter.d.ts.map +1 -0
- package/build/setup/deps/prompter.js +10 -0
- package/build/setup/deps/prompter.js.map +1 -0
- package/build/setup/errors.d.ts +27 -0
- package/build/setup/errors.d.ts.map +1 -0
- package/build/setup/errors.js +36 -0
- package/build/setup/errors.js.map +1 -0
- package/build/setup/options.d.ts +124 -0
- package/build/setup/options.d.ts.map +1 -0
- package/build/setup/options.js +64 -0
- package/build/setup/options.js.map +1 -0
- package/build/setup/prompts/instrumentationPrompt.d.ts +48 -0
- package/build/setup/prompts/instrumentationPrompt.d.ts.map +1 -0
- package/build/setup/prompts/instrumentationPrompt.js +68 -0
- package/build/setup/prompts/instrumentationPrompt.js.map +1 -0
- package/build/setup/runSetup.d.ts +48 -0
- package/build/setup/runSetup.d.ts.map +1 -0
- package/build/setup/runSetup.js +213 -0
- package/build/setup/runSetup.js.map +1 -0
- package/build/setup/steps/chooseEndpoint.d.ts +36 -0
- package/build/setup/steps/chooseEndpoint.d.ts.map +1 -0
- package/build/setup/steps/chooseEndpoint.js +125 -0
- package/build/setup/steps/chooseEndpoint.js.map +1 -0
- package/build/setup/steps/confirmGitSafety.d.ts +17 -0
- package/build/setup/steps/confirmGitSafety.d.ts.map +1 -0
- package/build/setup/steps/confirmGitSafety.js +73 -0
- package/build/setup/steps/confirmGitSafety.js.map +1 -0
- package/build/setup/steps/establishConnection.d.ts +32 -0
- package/build/setup/steps/establishConnection.d.ts.map +1 -0
- package/build/setup/steps/establishConnection.js +252 -0
- package/build/setup/steps/establishConnection.js.map +1 -0
- package/build/setup/steps/installTooling.d.ts +28 -0
- package/build/setup/steps/installTooling.d.ts.map +1 -0
- package/build/setup/steps/installTooling.js +79 -0
- package/build/setup/steps/installTooling.js.map +1 -0
- package/build/setup/steps/instrumentApp.d.ts +60 -0
- package/build/setup/steps/instrumentApp.d.ts.map +1 -0
- package/build/setup/steps/instrumentApp.js +179 -0
- package/build/setup/steps/instrumentApp.js.map +1 -0
- package/build/setup/steps/offerDocsMcp.d.ts +48 -0
- package/build/setup/steps/offerDocsMcp.d.ts.map +1 -0
- package/build/setup/steps/offerDocsMcp.js +133 -0
- package/build/setup/steps/offerDocsMcp.js.map +1 -0
- package/build/setup/steps/offerPxProfile.d.ts +20 -0
- package/build/setup/steps/offerPxProfile.d.ts.map +1 -0
- package/build/setup/steps/offerPxProfile.js +95 -0
- package/build/setup/steps/offerPxProfile.js.map +1 -0
- package/build/setup/steps/prefetchDocs.d.ts +24 -0
- package/build/setup/steps/prefetchDocs.d.ts.map +1 -0
- package/build/setup/steps/prefetchDocs.js +49 -0
- package/build/setup/steps/prefetchDocs.js.map +1 -0
- package/build/setup/steps/verifyTraces.d.ts +44 -0
- package/build/setup/steps/verifyTraces.d.ts.map +1 -0
- package/build/setup/steps/verifyTraces.js +119 -0
- package/build/setup/steps/verifyTraces.js.map +1 -0
- package/build/setup/steps/writeEnvFile.d.ts +19 -0
- package/build/setup/steps/writeEnvFile.d.ts.map +1 -0
- package/build/setup/steps/writeEnvFile.js +50 -0
- package/build/setup/steps/writeEnvFile.js.map +1 -0
- package/build/setup/ui/clackPrompter.d.ts +11 -0
- package/build/setup/ui/clackPrompter.d.ts.map +1 -0
- package/build/setup/ui/clackPrompter.js +122 -0
- package/build/setup/ui/clackPrompter.js.map +1 -0
- package/build/setup/ui/selectBoolean.d.ts +18 -0
- package/build/setup/ui/selectBoolean.d.ts.map +1 -0
- package/build/setup/ui/selectBoolean.js +14 -0
- package/build/setup/ui/selectBoolean.js.map +1 -0
- package/build/setup/util/gitignoreCoverage.d.ts +24 -0
- package/build/setup/util/gitignoreCoverage.d.ts.map +1 -0
- package/build/setup/util/gitignoreCoverage.js +43 -0
- package/build/setup/util/gitignoreCoverage.js.map +1 -0
- package/build/setup/util/mcpConfig.d.ts +31 -0
- package/build/setup/util/mcpConfig.d.ts.map +1 -0
- package/build/setup/util/mcpConfig.js +62 -0
- package/build/setup/util/mcpConfig.js.map +1 -0
- package/build/setup/util/paths.d.ts +6 -0
- package/build/setup/util/paths.d.ts.map +1 -0
- package/build/setup/util/paths.js +23 -0
- package/build/setup/util/paths.js.map +1 -0
- package/build/setup/util/redact.d.ts +12 -0
- package/build/setup/util/redact.d.ts.map +1 -0
- package/build/setup/util/redact.js +31 -0
- package/build/setup/util/redact.js.map +1 -0
- package/build/validation/endpoint.d.ts +19 -0
- package/build/validation/endpoint.d.ts.map +1 -0
- package/build/validation/endpoint.js +39 -0
- package/build/validation/endpoint.js.map +1 -0
- package/package.json +15 -12
- package/build/commands/annotationMutationUtils.d.ts.map +0 -1
- package/build/commands/annotationMutationUtils.js.map +0 -1
- package/build/commands/noteMutationUtils.d.ts.map +0 -1
- package/build/commands/noteMutationUtils.js.map +0 -1
- package/build/commands/sessionAnnotationsCommand.d.ts.map +0 -1
- package/build/commands/sessionAnnotationsCommand.js.map +0 -1
- package/build/commands/spanAnnotationsCommand.d.ts +0 -7
- package/build/commands/spanAnnotationsCommand.d.ts.map +0 -1
- package/build/commands/spanAnnotationsCommand.js +0 -164
- package/build/commands/spanAnnotationsCommand.js.map +0 -1
- package/build/commands/traceAnnotationsCommand.d.ts +0 -7
- package/build/commands/traceAnnotationsCommand.d.ts.map +0 -1
- package/build/commands/traceAnnotationsCommand.js +0 -167
- package/build/commands/traceAnnotationsCommand.js.map +0 -1
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Choose the endpoint: where Phoenix is, and whether it wants an API key.
|
|
3
|
+
*
|
|
4
|
+
* Two options — Local or Remote ("paste your instance URL"). The probe is
|
|
5
|
+
* an unauthenticated
|
|
6
|
+
* `GET /v1/projects?limit=1`: 200 → auth off, 401/403 → auth on, anything
|
|
7
|
+
* else → troubleshoot copy and re-ask (max 3 attempts).
|
|
8
|
+
*/
|
|
9
|
+
import { HttpError } from "@arizeai/phoenix-client";
|
|
10
|
+
import { isEndpointUrl, normalizeEndpoint } from "../../validation/endpoint.js";
|
|
11
|
+
import * as COPY from "../copy.js";
|
|
12
|
+
import { HeadlessInputError, SetupCancelledError, SetupFatalError, } from "../errors.js";
|
|
13
|
+
import { redactForDisplay } from "../util/redact.js";
|
|
14
|
+
const PROBE_TIMEOUT_MS = 10_000;
|
|
15
|
+
const MAX_ATTEMPTS = 3;
|
|
16
|
+
const DEFAULT_LOCAL_ENDPOINT = "http://localhost:6006";
|
|
17
|
+
/**
|
|
18
|
+
* Probe an endpoint to determine reachability and whether auth is enabled.
|
|
19
|
+
*/
|
|
20
|
+
export async function probeEndpoint(deps, endpoint) {
|
|
21
|
+
const client = deps.createClient({ endpoint });
|
|
22
|
+
try {
|
|
23
|
+
await client.GET("/v1/projects", {
|
|
24
|
+
params: { query: { limit: 1 } },
|
|
25
|
+
signal: AbortSignal.timeout(PROBE_TIMEOUT_MS),
|
|
26
|
+
});
|
|
27
|
+
return { kind: "authOff" };
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
if (error instanceof HttpError) {
|
|
31
|
+
if (error.status === 401 || error.status === 403) {
|
|
32
|
+
return { kind: "authOn" };
|
|
33
|
+
}
|
|
34
|
+
return { kind: "notPhoenix", detail: `HTTP ${error.status}` };
|
|
35
|
+
}
|
|
36
|
+
// A 2xx whose body will not parse: something is listening, but it is not
|
|
37
|
+
// the Phoenix API.
|
|
38
|
+
if (error instanceof SyntaxError) {
|
|
39
|
+
return { kind: "notPhoenix", detail: COPY.ENDPOINT.unexpectedBody };
|
|
40
|
+
}
|
|
41
|
+
return { kind: "unreachable", detail: redactForDisplay(String(error)) };
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async function promptForEndpoint(deps) {
|
|
45
|
+
const choice = await deps.prompter.select({
|
|
46
|
+
message: COPY.ENDPOINT.selectMessage,
|
|
47
|
+
options: [
|
|
48
|
+
{
|
|
49
|
+
value: "local",
|
|
50
|
+
label: COPY.ENDPOINT.localLabel,
|
|
51
|
+
hint: COPY.ENDPOINT.localHint,
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
value: "remote",
|
|
55
|
+
label: COPY.ENDPOINT.remoteLabel,
|
|
56
|
+
hint: COPY.ENDPOINT.remoteHint,
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
});
|
|
60
|
+
if (choice === "local") {
|
|
61
|
+
return DEFAULT_LOCAL_ENDPOINT;
|
|
62
|
+
}
|
|
63
|
+
const url = await deps.prompter.textInput({
|
|
64
|
+
message: COPY.ENDPOINT.remoteUrlMessage,
|
|
65
|
+
validate: (value) => isEndpointUrl(value) ? undefined : COPY.ENDPOINT.remoteUrlInvalid,
|
|
66
|
+
});
|
|
67
|
+
return url;
|
|
68
|
+
}
|
|
69
|
+
export async function chooseEndpoint(deps, { presetEndpoint, headless }) {
|
|
70
|
+
let attempts = 0;
|
|
71
|
+
let candidate = presetEndpoint;
|
|
72
|
+
for (;;) {
|
|
73
|
+
if (candidate === undefined) {
|
|
74
|
+
if (headless) {
|
|
75
|
+
throw new HeadlessInputError(COPY.ENDPOINT.headlessNeedsEndpoint);
|
|
76
|
+
}
|
|
77
|
+
candidate = await promptForEndpoint(deps);
|
|
78
|
+
}
|
|
79
|
+
let endpoint;
|
|
80
|
+
try {
|
|
81
|
+
endpoint = normalizeEndpoint(candidate);
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
if (headless) {
|
|
85
|
+
throw new HeadlessInputError(COPY.ENDPOINT.remoteUrlInvalid);
|
|
86
|
+
}
|
|
87
|
+
deps.prompter.line(COPY.ENDPOINT.remoteUrlInvalid);
|
|
88
|
+
candidate = undefined;
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
deps.prompter.line(COPY.ENDPOINT.probing(endpoint));
|
|
92
|
+
const outcome = await probeEndpoint(deps, endpoint);
|
|
93
|
+
if (outcome.kind === "authOff") {
|
|
94
|
+
deps.prompter.line(COPY.ENDPOINT.authOff);
|
|
95
|
+
return { endpoint, authEnabled: false };
|
|
96
|
+
}
|
|
97
|
+
if (outcome.kind === "authOn") {
|
|
98
|
+
deps.prompter.line(COPY.ENDPOINT.authOn);
|
|
99
|
+
return { endpoint, authEnabled: true };
|
|
100
|
+
}
|
|
101
|
+
if (headless) {
|
|
102
|
+
throw new SetupFatalError(COPY.ENDPOINT.headlessUnreachable(endpoint));
|
|
103
|
+
}
|
|
104
|
+
attempts += 1;
|
|
105
|
+
deps.prompter.line(outcome.kind === "unreachable"
|
|
106
|
+
? COPY.ENDPOINT.unreachable(endpoint)
|
|
107
|
+
: COPY.ENDPOINT.notPhoenix(endpoint, outcome.detail));
|
|
108
|
+
if (attempts >= MAX_ATTEMPTS) {
|
|
109
|
+
deps.prompter.line(COPY.ENDPOINT.gaveUp);
|
|
110
|
+
throw new SetupCancelledError();
|
|
111
|
+
}
|
|
112
|
+
const retry = await deps.prompter.select({
|
|
113
|
+
message: COPY.ENDPOINT.retryMessage,
|
|
114
|
+
options: [
|
|
115
|
+
{ value: true, label: COPY.ENDPOINT.retryYes },
|
|
116
|
+
{ value: false, label: COPY.ENDPOINT.retryNo },
|
|
117
|
+
],
|
|
118
|
+
});
|
|
119
|
+
if (!retry) {
|
|
120
|
+
throw new SetupCancelledError();
|
|
121
|
+
}
|
|
122
|
+
candidate = undefined;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=chooseEndpoint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chooseEndpoint.js","sourceRoot":"","sources":["../../../src/setup/steps/chooseEndpoint.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Confirm git can undo whatever setup is about to do.
|
|
3
|
+
*
|
|
4
|
+
* A coding agent may be about to edit files in this directory; git is the
|
|
5
|
+
* undo button. Both gates are opt-in selects with the safe option first.
|
|
6
|
+
* Headless mode proceeds only on a clean repo.
|
|
7
|
+
*/
|
|
8
|
+
import type { SetupDeps } from "../deps/index.js";
|
|
9
|
+
export interface GitSafetyResult {
|
|
10
|
+
isGitRepository: boolean;
|
|
11
|
+
/** False when the user chose to stop at a gate. */
|
|
12
|
+
proceed: boolean;
|
|
13
|
+
}
|
|
14
|
+
export declare function confirmGitSafety(deps: Pick<SetupDeps, "context" | "processes" | "prompter">, { headless }: {
|
|
15
|
+
headless: boolean;
|
|
16
|
+
}): Promise<GitSafetyResult>;
|
|
17
|
+
//# sourceMappingURL=confirmGitSafety.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"confirmGitSafety.d.ts","sourceRoot":"","sources":["../../../src/setup/steps/confirmGitSafety.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAIzC,MAAM,WAAW,eAAe;IAC9B,eAAe,EAAE,OAAO,CAAC;IACzB,mDAAmD;IACnD,OAAO,EAAE,OAAO,CAAC;CAClB;AAID,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,IAAI,CAAC,SAAS,EAAE,SAAS,GAAG,WAAW,GAAG,UAAU,CAAC,EAC3D,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,OAAO,CAAA;CAAE,GAClC,OAAO,CAAC,eAAe,CAAC,CAmE1B"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Confirm git can undo whatever setup is about to do.
|
|
3
|
+
*
|
|
4
|
+
* A coding agent may be about to edit files in this directory; git is the
|
|
5
|
+
* undo button. Both gates are opt-in selects with the safe option first.
|
|
6
|
+
* Headless mode proceeds only on a clean repo.
|
|
7
|
+
*/
|
|
8
|
+
import * as COPY from "../copy.js";
|
|
9
|
+
import { SetupFatalError } from "../errors.js";
|
|
10
|
+
import { selectBoolean } from "../ui/selectBoolean.js";
|
|
11
|
+
const MAX_LISTED_FILES = 20;
|
|
12
|
+
export async function confirmGitSafety(deps, { headless }) {
|
|
13
|
+
const repoCheck = await deps.processes.exec({
|
|
14
|
+
command: "git",
|
|
15
|
+
args: ["rev-parse", "--is-inside-work-tree"],
|
|
16
|
+
cwd: deps.context.cwd,
|
|
17
|
+
});
|
|
18
|
+
const isGitRepository = repoCheck.exitCode === 0 && repoCheck.stdout.trim() === "true";
|
|
19
|
+
if (!isGitRepository) {
|
|
20
|
+
// `git` missing entirely is treated the same as "not a repo".
|
|
21
|
+
if (headless) {
|
|
22
|
+
throw new SetupFatalError(COPY.GIT.headlessNotARepo);
|
|
23
|
+
}
|
|
24
|
+
const proceed = await selectBoolean({
|
|
25
|
+
prompter: deps.prompter,
|
|
26
|
+
message: COPY.GIT.notARepoMessage,
|
|
27
|
+
noFirst: true,
|
|
28
|
+
noLabel: COPY.GIT.notARepoNo,
|
|
29
|
+
noHint: COPY.GIT.notARepoNoHint,
|
|
30
|
+
yesLabel: COPY.GIT.notARepoYes,
|
|
31
|
+
yesHint: COPY.GIT.notARepoYesHint,
|
|
32
|
+
});
|
|
33
|
+
if (!proceed) {
|
|
34
|
+
deps.prompter.line(COPY.GIT.stopped);
|
|
35
|
+
}
|
|
36
|
+
return { isGitRepository, proceed };
|
|
37
|
+
}
|
|
38
|
+
const statusCheck = await deps.processes.exec({
|
|
39
|
+
command: "git",
|
|
40
|
+
args: ["status", "--porcelain=v1"],
|
|
41
|
+
cwd: deps.context.cwd,
|
|
42
|
+
});
|
|
43
|
+
const dirtyPaths = statusCheck.stdout
|
|
44
|
+
.split("\n")
|
|
45
|
+
.map((line) => line.trim())
|
|
46
|
+
.filter(Boolean);
|
|
47
|
+
if (dirtyPaths.length === 0) {
|
|
48
|
+
return { isGitRepository, proceed: true };
|
|
49
|
+
}
|
|
50
|
+
if (headless) {
|
|
51
|
+
throw new SetupFatalError(COPY.GIT.headlessDirty);
|
|
52
|
+
}
|
|
53
|
+
const shown = dirtyPaths.slice(0, MAX_LISTED_FILES);
|
|
54
|
+
const lines = [...shown];
|
|
55
|
+
if (dirtyPaths.length > MAX_LISTED_FILES) {
|
|
56
|
+
lines.push(COPY.GIT.andMore(dirtyPaths.length - MAX_LISTED_FILES));
|
|
57
|
+
}
|
|
58
|
+
deps.prompter.note(lines.join("\n"), COPY.GIT.dirtyFileListTitle);
|
|
59
|
+
const proceed = await selectBoolean({
|
|
60
|
+
prompter: deps.prompter,
|
|
61
|
+
message: COPY.GIT.dirtyMessage(dirtyPaths.length),
|
|
62
|
+
noFirst: true,
|
|
63
|
+
noLabel: COPY.GIT.dirtyNo,
|
|
64
|
+
noHint: COPY.GIT.dirtyNoHint,
|
|
65
|
+
yesLabel: COPY.GIT.dirtyYes,
|
|
66
|
+
yesHint: COPY.GIT.dirtyYesHint,
|
|
67
|
+
});
|
|
68
|
+
if (!proceed) {
|
|
69
|
+
deps.prompter.line(COPY.GIT.stopped);
|
|
70
|
+
}
|
|
71
|
+
return { isGitRepository, proceed };
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=confirmGitSafety.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"confirmGitSafety.js","sourceRoot":"","sources":["../../../src/setup/steps/confirmGitSafety.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Establish the connection.
|
|
3
|
+
*
|
|
4
|
+
* The project itself is never created here — Phoenix creates a project on
|
|
5
|
+
* first ingestion, so setup only needs to settle on a *name* and hand it
|
|
6
|
+
* to the app via `PHOENIX_PROJECT_NAME`. What remains is credential
|
|
7
|
+
* acquisition: the auth-on lanes verify the key with a read-only probe so a
|
|
8
|
+
* bad paste is caught here rather than at first trace. When the instance runs
|
|
9
|
+
* the OAuth2 authorization server, the interactive lane offers a streamlined
|
|
10
|
+
* path first — log in with the browser, mint an API key over REST, revoke the
|
|
11
|
+
* grant that login created — and every failure along it falls back to the
|
|
12
|
+
* paste prompt rather than aborting setup.
|
|
13
|
+
*/
|
|
14
|
+
import type { SetupDeps } from "../deps/index.js";
|
|
15
|
+
import type { SetupInputs } from "../options.js";
|
|
16
|
+
export interface Connection {
|
|
17
|
+
/** Normalized origin, no trailing slash. */
|
|
18
|
+
endpoint: string;
|
|
19
|
+
projectName: string;
|
|
20
|
+
/** Present iff auth is enabled on the deployment. */
|
|
21
|
+
apiKey?: string;
|
|
22
|
+
}
|
|
23
|
+
/** Names are used as URL path identifiers; '/', '?', '#' are invalid. */
|
|
24
|
+
export declare function validateProjectName(value: string): string | undefined;
|
|
25
|
+
export declare function defaultProjectName(cwd: string): string;
|
|
26
|
+
export interface EstablishConnectionArgs {
|
|
27
|
+
endpoint: string;
|
|
28
|
+
authEnabled: boolean;
|
|
29
|
+
inputs: SetupInputs;
|
|
30
|
+
}
|
|
31
|
+
export declare function establishConnection(deps: Pick<SetupDeps, "context" | "createClient" | "prompter" | "oauthLogin">, { endpoint, authEnabled, inputs }: EstablishConnectionArgs): Promise<Connection>;
|
|
32
|
+
//# sourceMappingURL=establishConnection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"establishConnection.d.ts","sourceRoot":"","sources":["../../../src/setup/steps/establishConnection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAMH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAG9C,MAAM,WAAW,UAAU;IACzB,4CAA4C;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,qDAAqD;IACrD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAQD,yEAAyE;AACzE,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAMrE;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAGtD;AA6QD,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,OAAO,CAAC;IACrB,MAAM,EAAE,WAAW,CAAC;CACrB;AAED,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,IAAI,CAAC,SAAS,EAAE,SAAS,GAAG,cAAc,GAAG,UAAU,GAAG,YAAY,CAAC,EAC7E,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAE,EAAE,uBAAuB,GACzD,OAAO,CAAC,UAAU,CAAC,CAgBrB"}
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Establish the connection.
|
|
3
|
+
*
|
|
4
|
+
* The project itself is never created here — Phoenix creates a project on
|
|
5
|
+
* first ingestion, so setup only needs to settle on a *name* and hand it
|
|
6
|
+
* to the app via `PHOENIX_PROJECT_NAME`. What remains is credential
|
|
7
|
+
* acquisition: the auth-on lanes verify the key with a read-only probe so a
|
|
8
|
+
* bad paste is caught here rather than at first trace. When the instance runs
|
|
9
|
+
* the OAuth2 authorization server, the interactive lane offers a streamlined
|
|
10
|
+
* path first — log in with the browser, mint an API key over REST, revoke the
|
|
11
|
+
* grant that login created — and every failure along it falls back to the
|
|
12
|
+
* paste prompt rather than aborting setup.
|
|
13
|
+
*/
|
|
14
|
+
import * as path from "node:path";
|
|
15
|
+
import { HttpError } from "@arizeai/phoenix-client";
|
|
16
|
+
import * as COPY from "../copy.js";
|
|
17
|
+
import { HeadlessInputError, SetupFatalError } from "../errors.js";
|
|
18
|
+
import { redactForDisplay } from "../util/redact.js";
|
|
19
|
+
const REST_TIMEOUT_MS = 10_000;
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// Project name
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
/** Names are used as URL path identifiers; '/', '?', '#' are invalid. */
|
|
24
|
+
export function validateProjectName(value) {
|
|
25
|
+
const trimmed = value.trim();
|
|
26
|
+
if (!trimmed || /[/?#]/.test(trimmed)) {
|
|
27
|
+
return COPY.CONNECT.projectNameInvalid;
|
|
28
|
+
}
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
export function defaultProjectName(cwd) {
|
|
32
|
+
const base = path.basename(cwd).replace(/[/?#]/g, "-").trim();
|
|
33
|
+
return base || "default";
|
|
34
|
+
}
|
|
35
|
+
async function promptForProjectName(deps, inputs) {
|
|
36
|
+
const name = inputs.project ??
|
|
37
|
+
(await deps.prompter.textInput({
|
|
38
|
+
message: COPY.CONNECT.projectNameMessage,
|
|
39
|
+
defaultValue: defaultProjectName(deps.context.cwd),
|
|
40
|
+
validate: validateProjectName,
|
|
41
|
+
}));
|
|
42
|
+
return name.trim();
|
|
43
|
+
}
|
|
44
|
+
/** A REST failure, as a short phrase to show the user. */
|
|
45
|
+
function describeRestError(error) {
|
|
46
|
+
if (error instanceof HttpError) {
|
|
47
|
+
return `HTTP ${error.status}`;
|
|
48
|
+
}
|
|
49
|
+
// A 2xx whose body will not parse: something answered, but not the Phoenix
|
|
50
|
+
// API. The raw parser message would only confuse.
|
|
51
|
+
if (error instanceof SyntaxError) {
|
|
52
|
+
return COPY.ENDPOINT.unexpectedBody;
|
|
53
|
+
}
|
|
54
|
+
return redactForDisplay(String(error));
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Read-only probe: list one project. Only the credential matters here — an
|
|
58
|
+
* instance with no projects yet still answers 200.
|
|
59
|
+
*/
|
|
60
|
+
async function checkApiKey(deps, endpoint, apiKey) {
|
|
61
|
+
const client = deps.createClient({ endpoint, apiKey });
|
|
62
|
+
try {
|
|
63
|
+
await client.GET("/v1/projects", {
|
|
64
|
+
params: { query: { limit: 1 } },
|
|
65
|
+
signal: AbortSignal.timeout(REST_TIMEOUT_MS),
|
|
66
|
+
});
|
|
67
|
+
return { kind: "ok" };
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
if (error instanceof HttpError &&
|
|
71
|
+
(error.status === 401 || error.status === 403)) {
|
|
72
|
+
return { kind: "rejected", status: error.status };
|
|
73
|
+
}
|
|
74
|
+
return { kind: "error", detail: describeRestError(error) };
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// ---------------------------------------------------------------------------
|
|
78
|
+
// Interactive lanes
|
|
79
|
+
// ---------------------------------------------------------------------------
|
|
80
|
+
async function connectAuthOff(deps, endpoint, inputs) {
|
|
81
|
+
const projectName = await promptForProjectName(deps, inputs);
|
|
82
|
+
deps.prompter.line(COPY.CONNECT.usingProject(projectName));
|
|
83
|
+
return { endpoint, projectName };
|
|
84
|
+
}
|
|
85
|
+
/** Prompt for an API key without echoing it to the terminal. */
|
|
86
|
+
async function promptForApiKey(deps) {
|
|
87
|
+
const apiKey = await deps.prompter.passwordInput({
|
|
88
|
+
message: COPY.API_KEY.pasteKeyMessage,
|
|
89
|
+
validate: (value) => value.trim() ? undefined : COPY.API_KEY.pasteKeyInvalid,
|
|
90
|
+
});
|
|
91
|
+
return apiKey.trim();
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Log in via the browser, then mint a personal API key over REST. Every
|
|
95
|
+
* failure returns `undefined` after narrating why, sending the lane to the
|
|
96
|
+
* paste prompt — the login is a shortcut, never a wall.
|
|
97
|
+
*/
|
|
98
|
+
async function acquireApiKeyViaLogin(deps, endpoint, projectName) {
|
|
99
|
+
// Ctrl-C here means "give up on the browser", not "abandon setup" — the
|
|
100
|
+
// paste prompt below is still a way forward, and the project name the user
|
|
101
|
+
// already gave is still good.
|
|
102
|
+
const outcome = await deps.prompter.runInterruptible((signal) => deps.oauthLogin.login({
|
|
103
|
+
endpoint,
|
|
104
|
+
onAuthorizationUrl: (url) => deps.prompter.note(COPY.CREDENTIALS.authorizationUrl(url)),
|
|
105
|
+
onBrowserOpenFailed: (detail) => deps.prompter.line(COPY.CREDENTIALS.browserOpenFailed(redactForDisplay(detail))),
|
|
106
|
+
signal,
|
|
107
|
+
}));
|
|
108
|
+
if (outcome.status === "cancelled") {
|
|
109
|
+
deps.prompter.line(COPY.CREDENTIALS.loginCancelled);
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
if (outcome.status === "error") {
|
|
113
|
+
deps.prompter.line(COPY.CREDENTIALS.loginFailed(redactForDisplay(outcome.detail)));
|
|
114
|
+
return undefined;
|
|
115
|
+
}
|
|
116
|
+
try {
|
|
117
|
+
const client = deps.createClient({
|
|
118
|
+
endpoint,
|
|
119
|
+
apiKey: outcome.accessToken,
|
|
120
|
+
});
|
|
121
|
+
const name = `px-setup ${projectName}`;
|
|
122
|
+
const response = await client.POST("/v1/user/api_keys", {
|
|
123
|
+
body: {
|
|
124
|
+
data: {
|
|
125
|
+
name,
|
|
126
|
+
description: `Created by px setup for project "${projectName}".`,
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
signal: AbortSignal.timeout(REST_TIMEOUT_MS),
|
|
130
|
+
});
|
|
131
|
+
const key = response.data?.data.key;
|
|
132
|
+
if (!key) {
|
|
133
|
+
deps.prompter.line(COPY.CREDENTIALS.keyCreateFailed("empty key in response"));
|
|
134
|
+
return undefined;
|
|
135
|
+
}
|
|
136
|
+
deps.prompter.line(COPY.CREDENTIALS.keyCreated(name));
|
|
137
|
+
return key;
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
deps.prompter.line(COPY.CREDENTIALS.keyCreateFailed(describeRestError(error)));
|
|
141
|
+
return undefined;
|
|
142
|
+
}
|
|
143
|
+
finally {
|
|
144
|
+
// The access token has served its one purpose. Ending the grant leaves the
|
|
145
|
+
// key in local files as the only credential this run created — without it,
|
|
146
|
+
// a refresh token setup no longer holds stays live for its full lifetime
|
|
147
|
+
// (a week by default). This does not touch an existing `px auth login`.
|
|
148
|
+
await outcome.revoke().catch(() => { });
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* The project name comes first so the browser-login path can stamp it into
|
|
153
|
+
* the minted key's name. A rejected key returns to the masked credential
|
|
154
|
+
* prompt without asking for the project name again.
|
|
155
|
+
*/
|
|
156
|
+
async function connectAuthOnInteractive(deps, endpoint, inputs) {
|
|
157
|
+
// Start the probe before the prompt so it can run while the user types (when
|
|
158
|
+
// --project is given there is no prompt, and this is simply awaited below).
|
|
159
|
+
// The `catch` is not redundant: the contract returns Promise<boolean>, and an
|
|
160
|
+
// implementation that rejected would otherwise go unhandled on the path where
|
|
161
|
+
// the prompt throws SetupCancelledError before the await below is reached.
|
|
162
|
+
const supportsLogin = deps.oauthLogin
|
|
163
|
+
.isSupported(endpoint)
|
|
164
|
+
.catch(() => false);
|
|
165
|
+
const projectName = await promptForProjectName(deps, inputs);
|
|
166
|
+
if (await supportsLogin) {
|
|
167
|
+
const method = await deps.prompter.select({
|
|
168
|
+
message: COPY.CREDENTIALS.methodMessage,
|
|
169
|
+
options: [
|
|
170
|
+
{
|
|
171
|
+
value: "login",
|
|
172
|
+
label: COPY.CREDENTIALS.loginLabel,
|
|
173
|
+
hint: COPY.CREDENTIALS.loginHint,
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
value: "paste",
|
|
177
|
+
label: COPY.CREDENTIALS.pasteLabel,
|
|
178
|
+
hint: COPY.CREDENTIALS.pasteHint,
|
|
179
|
+
},
|
|
180
|
+
],
|
|
181
|
+
});
|
|
182
|
+
if (method === "login") {
|
|
183
|
+
const apiKey = await acquireApiKeyViaLogin(deps, endpoint, projectName);
|
|
184
|
+
if (apiKey !== undefined) {
|
|
185
|
+
// The key was just minted by an authenticated call to this same
|
|
186
|
+
// endpoint, so a probe could only re-prove what the mint proved. It
|
|
187
|
+
// could, however, fail transiently — and the ephemeral session is
|
|
188
|
+
// already revoked, so a throw here would strand a live key the user
|
|
189
|
+
// never received and cannot name.
|
|
190
|
+
deps.prompter.line(COPY.CONNECT.usingProject(projectName));
|
|
191
|
+
return { endpoint, projectName, apiKey };
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
deps.prompter.note(COPY.API_KEY.instructions, COPY.API_KEY.instructionsTitle);
|
|
196
|
+
let apiKey = await promptForApiKey(deps);
|
|
197
|
+
for (;;) {
|
|
198
|
+
const result = await checkApiKey(deps, endpoint, apiKey);
|
|
199
|
+
if (result.kind === "ok") {
|
|
200
|
+
deps.prompter.line(COPY.CONNECT.usingProject(projectName));
|
|
201
|
+
return { endpoint, projectName, apiKey };
|
|
202
|
+
}
|
|
203
|
+
if (result.kind === "rejected") {
|
|
204
|
+
deps.prompter.line(COPY.API_KEY.pasteKeyRejected);
|
|
205
|
+
apiKey = await promptForApiKey(deps);
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
throw new SetupFatalError(COPY.CONNECT.connectFailed(result.detail));
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
// ---------------------------------------------------------------------------
|
|
212
|
+
// Headless lane
|
|
213
|
+
// ---------------------------------------------------------------------------
|
|
214
|
+
async function connectHeadless(deps, endpoint, authEnabled, inputs) {
|
|
215
|
+
if (!inputs.project) {
|
|
216
|
+
throw new HeadlessInputError(COPY.CONNECT.headlessNeedsProject);
|
|
217
|
+
}
|
|
218
|
+
const projectName = inputs.project.trim();
|
|
219
|
+
if (!authEnabled) {
|
|
220
|
+
return { endpoint, projectName };
|
|
221
|
+
}
|
|
222
|
+
const apiKey = inputs.apiKey;
|
|
223
|
+
if (!apiKey) {
|
|
224
|
+
throw new HeadlessInputError(COPY.CONNECT.headlessNeedsApiKey);
|
|
225
|
+
}
|
|
226
|
+
const result = await checkApiKey(deps, endpoint, apiKey);
|
|
227
|
+
if (result.kind === "rejected") {
|
|
228
|
+
throw new SetupFatalError(COPY.CONNECT.headlessAuthRejected);
|
|
229
|
+
}
|
|
230
|
+
if (result.kind === "error") {
|
|
231
|
+
throw new SetupFatalError(COPY.CONNECT.connectFailed(result.detail));
|
|
232
|
+
}
|
|
233
|
+
return { endpoint, projectName, apiKey };
|
|
234
|
+
}
|
|
235
|
+
export async function establishConnection(deps, { endpoint, authEnabled, inputs }) {
|
|
236
|
+
// A --project / env-provided name bypasses the prompt in every lane, so
|
|
237
|
+
// enforce here the same rule the prompt validates interactively.
|
|
238
|
+
if (inputs.project !== undefined) {
|
|
239
|
+
const nameError = validateProjectName(inputs.project);
|
|
240
|
+
if (nameError) {
|
|
241
|
+
throw new HeadlessInputError(nameError);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
if (inputs.headless) {
|
|
245
|
+
return connectHeadless(deps, endpoint, authEnabled, inputs);
|
|
246
|
+
}
|
|
247
|
+
if (!authEnabled) {
|
|
248
|
+
return connectAuthOff(deps, endpoint, inputs);
|
|
249
|
+
}
|
|
250
|
+
return connectAuthOnInteractive(deps, endpoint, inputs);
|
|
251
|
+
}
|
|
252
|
+
//# sourceMappingURL=establishConnection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"establishConnection.js","sourceRoot":"","sources":["../../../src/setup/steps/establishConnection.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Install the tooling: the global `px` CLI, and the Phoenix skills.
|
|
3
|
+
*
|
|
4
|
+
* Runs after traces are verified — the peak-motivation moment — and offers
|
|
5
|
+
* the tooling that turns "traces are flowing" into "I can look at them":
|
|
6
|
+
* a global `px` install when setup is running without `px` on PATH
|
|
7
|
+
* (the `npx @arizeai/phoenix-cli setup` case), and the Phoenix skills for
|
|
8
|
+
* the user's coding agent via `npx skills add` (no `--skill` pins — the
|
|
9
|
+
* skills CLI's own picker lists the repo's skills and target agents). Both
|
|
10
|
+
* installs are spawned on the terminal so their own output and prompts show
|
|
11
|
+
* through; failures are non-fatal warnings with the command to retry by hand.
|
|
12
|
+
*/
|
|
13
|
+
import type { SetupDeps } from "../deps/index.js";
|
|
14
|
+
/** What an install offer did — reported in the setup summary. */
|
|
15
|
+
export type ToolingOutcome = "installed" | "failed" | "declined" | "skipped";
|
|
16
|
+
export interface ToolingResult {
|
|
17
|
+
cli: ToolingOutcome;
|
|
18
|
+
skills: ToolingOutcome;
|
|
19
|
+
}
|
|
20
|
+
export declare function offerToolingInstalls(deps: Pick<SetupDeps, "context" | "processes" | "prompter">, { pxOnPath, skills, canPrompt, }: {
|
|
21
|
+
/** Started at setup launch so the probe never blocks this prompt. */
|
|
22
|
+
pxOnPath: Promise<boolean>;
|
|
23
|
+
/** `--skills` / `--no-skills`; undefined means ask (when we can). */
|
|
24
|
+
skills?: boolean;
|
|
25
|
+
/** False in a headless run: there is no terminal to ask on. */
|
|
26
|
+
canPrompt: boolean;
|
|
27
|
+
}): Promise<ToolingResult>;
|
|
28
|
+
//# sourceMappingURL=installTooling.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"installTooling.d.ts","sourceRoot":"","sources":["../../../src/setup/steps/installTooling.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAAe,SAAS,EAAE,MAAM,SAAS,CAAC;AAiCtD,iEAAiE;AACjE,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC;AAE7E,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,cAAc,CAAC;IACpB,MAAM,EAAE,cAAc,CAAC;CACxB;AAiCD,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,IAAI,CAAC,SAAS,EAAE,SAAS,GAAG,WAAW,GAAG,UAAU,CAAC,EAC3D,EACE,QAAQ,EACR,MAAM,EACN,SAAS,GACV,EAAE;IACD,qEAAqE;IACrE,QAAQ,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3B,qEAAqE;IACrE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,+DAA+D;IAC/D,SAAS,EAAE,OAAO,CAAC;CACpB,GACA,OAAO,CAAC,aAAa,CAAC,CAoBxB"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Install the tooling: the global `px` CLI, and the Phoenix skills.
|
|
3
|
+
*
|
|
4
|
+
* Runs after traces are verified — the peak-motivation moment — and offers
|
|
5
|
+
* the tooling that turns "traces are flowing" into "I can look at them":
|
|
6
|
+
* a global `px` install when setup is running without `px` on PATH
|
|
7
|
+
* (the `npx @arizeai/phoenix-cli setup` case), and the Phoenix skills for
|
|
8
|
+
* the user's coding agent via `npx skills add` (no `--skill` pins — the
|
|
9
|
+
* skills CLI's own picker lists the repo's skills and target agents). Both
|
|
10
|
+
* installs are spawned on the terminal so their own output and prompts show
|
|
11
|
+
* through; failures are non-fatal warnings with the command to retry by hand.
|
|
12
|
+
*/
|
|
13
|
+
import * as COPY from "../copy.js";
|
|
14
|
+
import { selectBoolean } from "../ui/selectBoolean.js";
|
|
15
|
+
const CLI_INSTALL_SPEC = {
|
|
16
|
+
command: "npm",
|
|
17
|
+
args: ["install", "-g", "@arizeai/phoenix-cli"],
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* `npx skills add`, as spawned. The leading `-y` is npx's own — install the
|
|
21
|
+
* `skills` package without asking.
|
|
22
|
+
*
|
|
23
|
+
* Unattended, the skills CLI needs its `--yes` too: it is interactive by
|
|
24
|
+
* default (a skill picker, an agent picker, a final confirm) and a missing TTY
|
|
25
|
+
* does not suppress those prompts, so without the flag a headless run hangs on
|
|
26
|
+
* stdin. With it, the installer takes every skill in the repo for the agents it
|
|
27
|
+
* detects. Interactively we leave it off, so the user drives those pickers.
|
|
28
|
+
*/
|
|
29
|
+
function skillsAddSpec(unattended) {
|
|
30
|
+
return {
|
|
31
|
+
command: "npx",
|
|
32
|
+
args: [
|
|
33
|
+
"-y",
|
|
34
|
+
"skills",
|
|
35
|
+
"add",
|
|
36
|
+
COPY.SKILLS_SOURCE,
|
|
37
|
+
...(unattended ? ["--yes"] : []),
|
|
38
|
+
],
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Install, honoring a decision the caller already made. `decided` short-circuits
|
|
43
|
+
* the prompt: `true` installs (`--skills`), `false` skips (`--no-skills`), and
|
|
44
|
+
* `undefined` asks — so a headless run, which has no terminal to ask on, must
|
|
45
|
+
* arrive here already decided.
|
|
46
|
+
*/
|
|
47
|
+
async function offerInstall(deps, copy, spec, decided) {
|
|
48
|
+
const optedIn = decided ??
|
|
49
|
+
(await selectBoolean({
|
|
50
|
+
prompter: deps.prompter,
|
|
51
|
+
message: copy.message,
|
|
52
|
+
yesLabel: copy.yes,
|
|
53
|
+
noLabel: copy.no,
|
|
54
|
+
}));
|
|
55
|
+
if (!optedIn) {
|
|
56
|
+
return "declined";
|
|
57
|
+
}
|
|
58
|
+
const { exitCode } = await deps.processes.spawnInteractive({
|
|
59
|
+
...spec,
|
|
60
|
+
cwd: deps.context.cwd,
|
|
61
|
+
});
|
|
62
|
+
deps.prompter.line(exitCode === 0 ? copy.installed : copy.failed);
|
|
63
|
+
return exitCode === 0 ? "installed" : "failed";
|
|
64
|
+
}
|
|
65
|
+
export async function offerToolingInstalls(deps, { pxOnPath, skills, canPrompt, }) {
|
|
66
|
+
// The global CLI install is prompt-only — it is offered when `px` is missing
|
|
67
|
+
// (an `npx` run), and skipped outright when we cannot ask, because installing
|
|
68
|
+
// a global npm package is not something to do to an unattended caller.
|
|
69
|
+
const cli = !canPrompt || (await pxOnPath)
|
|
70
|
+
? "skipped"
|
|
71
|
+
: await offerInstall(deps, COPY.TOOLING.cli, CLI_INSTALL_SPEC);
|
|
72
|
+
// With no prompt to fall back on, an unflagged skills install defaults to
|
|
73
|
+
// going ahead: the only reason to invoke this lane unattended is to install.
|
|
74
|
+
// `--no-skills` still declines. This default lives here, next to `canPrompt`,
|
|
75
|
+
// rather than in each caller.
|
|
76
|
+
const skillsOutcome = await offerInstall(deps, COPY.TOOLING.skills, skillsAddSpec(!canPrompt), canPrompt ? skills : (skills ?? true));
|
|
77
|
+
return { cli, skills: skillsOutcome };
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=installTooling.js.map
|