@hue-run/sdk 0.3.1 → 0.4.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/CLI.md +270 -47
- package/ENVIRONMENTS.md +10 -0
- package/EVALUATIONS.md +1 -1
- package/README.md +20 -4
- package/dist/client.d.ts +5 -5
- package/dist/client.js +13 -6
- package/dist/environment/tools.d.ts +6 -1
- package/dist/environment/tools.js +7 -1
- package/dist/environment/types.d.ts +6 -1
- package/dist/evals/environment-target.js +1 -1
- package/dist/receipt.js +36 -8
- package/dist/setup/application.d.ts +74 -0
- package/dist/setup/application.js +766 -0
- package/dist/setup/backend.d.ts +229 -0
- package/dist/setup/backend.js +855 -0
- package/dist/setup/checkpoint.js +100 -30
- package/dist/setup/cli.js +20 -4
- package/dist/setup/configure.d.ts +13 -0
- package/dist/setup/configure.js +454 -0
- package/dist/setup/credential.d.ts +2 -0
- package/dist/setup/credential.js +9 -0
- package/dist/setup/detect.js +4 -1
- package/dist/setup/installation.d.ts +118 -0
- package/dist/setup/installation.js +605 -0
- package/dist/setup/lock.d.ts +2 -0
- package/dist/setup/lock.js +38 -0
- package/dist/setup/machine.d.ts +1 -10
- package/dist/setup/machine.js +8 -7
- package/dist/setup/render.d.ts +3 -1
- package/dist/setup/render.js +209 -6
- package/dist/setup/runner.d.ts +26 -76
- package/dist/setup/runner.js +320 -45
- package/dist/setup/socket.d.ts +7 -0
- package/dist/setup/socket.js +144 -0
- package/dist/setup/source.d.ts +9 -0
- package/dist/setup/source.js +269 -0
- package/dist/setup/types.d.ts +16 -9
- package/dist/setup/types.js +1 -1
- package/dist/setup.d.ts +6 -2
- package/dist/setup.js +3 -0
- package/dist/types.d.ts +24 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -1
- package/setup-events.schema.json +16 -9
package/dist/receipt.js
CHANGED
|
@@ -31,7 +31,7 @@ function validId(value, length) {
|
|
|
31
31
|
function record(value) {
|
|
32
32
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
33
33
|
}
|
|
34
|
-
function parseReceipt(value, traceId, expected, origin) {
|
|
34
|
+
function parseReceipt(value, traceId, expected, origin, setup = false) {
|
|
35
35
|
if (!record(value) ||
|
|
36
36
|
value.traceId !== traceId ||
|
|
37
37
|
!Number.isSafeInteger(value.spanCount) ||
|
|
@@ -52,6 +52,24 @@ function parseReceipt(value, traceId, expected, origin) {
|
|
|
52
52
|
}
|
|
53
53
|
if (traceUrl.origin !== origin || traceUrl.username || traceUrl.password)
|
|
54
54
|
invalidResponse();
|
|
55
|
+
if (setup) {
|
|
56
|
+
const uuid = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/u;
|
|
57
|
+
if (traceUrl.href !== value.traceUrl ||
|
|
58
|
+
traceUrl.hash ||
|
|
59
|
+
!traceUrl.pathname.startsWith("/traces/") ||
|
|
60
|
+
!uuid.test(traceUrl.pathname.slice(8)))
|
|
61
|
+
invalidResponse();
|
|
62
|
+
const queryNames = [];
|
|
63
|
+
traceUrl.searchParams.forEach((value, name) => {
|
|
64
|
+
if (!["projectId", "organizationId"].includes(name) ||
|
|
65
|
+
queryNames.includes(name) ||
|
|
66
|
+
!uuid.test(value))
|
|
67
|
+
invalidResponse();
|
|
68
|
+
queryNames.push(name);
|
|
69
|
+
});
|
|
70
|
+
if (queryNames.length !== 2)
|
|
71
|
+
invalidResponse();
|
|
72
|
+
}
|
|
55
73
|
const matched = value.matchedSpanIds, missing = value.missingSpanIds;
|
|
56
74
|
if (!Array.isArray(matched) ||
|
|
57
75
|
!Array.isArray(missing) ||
|
|
@@ -128,6 +146,13 @@ async function pause(milliseconds, signal) {
|
|
|
128
146
|
}
|
|
129
147
|
/** Observe persisted evidence after the application and its exporter have finished. */
|
|
130
148
|
export async function verifyTrace(connection, traceId, options = {}) {
|
|
149
|
+
return verifyTraceAtPath(connection, traceId, options, false, fetch);
|
|
150
|
+
}
|
|
151
|
+
/** @internal Dedicated receipt path for setup credentials; ordinary clients stay unchanged. */
|
|
152
|
+
export async function verifySetupTrace(connection, traceId, options, fetcher, signal) {
|
|
153
|
+
return verifyTraceAtPath(connection, traceId, options, true, fetcher, signal);
|
|
154
|
+
}
|
|
155
|
+
async function verifyTraceAtPath(connection, traceId, options, setup, fetcher, signal) {
|
|
131
156
|
if (!validId(traceId, 32))
|
|
132
157
|
throw new TypeError("traceId must be a nonzero lowercase 32-character OpenTelemetry trace ID");
|
|
133
158
|
if (options === null || typeof options !== "object" || Array.isArray(options))
|
|
@@ -148,27 +173,28 @@ export async function verifyTrace(connection, traceId, options = {}) {
|
|
|
148
173
|
throw new TypeError("timeoutMillis must be greater than zero and at most 60000");
|
|
149
174
|
// Snapshot caller arrays so concurrent mutation cannot alter the verification criteria.
|
|
150
175
|
const expectedIds = [...expected], requiredFields = [...required];
|
|
151
|
-
const url = new URL(`/api/v1/traces/${traceId}/receipt`, connection.baseUrl);
|
|
176
|
+
const url = new URL(`/api/v1/${setup ? "setup/" : ""}traces/${traceId}/receipt`, connection.baseUrl);
|
|
152
177
|
for (const id of expectedIds)
|
|
153
178
|
url.searchParams.append("expectedSpanId", id);
|
|
154
179
|
const controller = new AbortController();
|
|
155
180
|
const deadline = performance.now() + timeout;
|
|
156
181
|
const timer = setTimeout(() => controller.abort(), timeout);
|
|
182
|
+
const requestSignal = signal ? AbortSignal.any([signal, controller.signal]) : controller.signal;
|
|
157
183
|
let receipt = null;
|
|
158
184
|
let delay = 250;
|
|
159
185
|
try {
|
|
160
|
-
while (!
|
|
161
|
-
const response = await
|
|
186
|
+
while (!requestSignal.aborted && performance.now() < deadline) {
|
|
187
|
+
const response = await fetcher(url, {
|
|
162
188
|
headers: { Authorization: `Bearer ${connection.apiKey}`, Accept: "application/json" },
|
|
163
189
|
redirect: "manual",
|
|
164
190
|
credentials: "omit",
|
|
165
191
|
cache: "no-store",
|
|
166
|
-
signal:
|
|
192
|
+
signal: requestSignal,
|
|
167
193
|
});
|
|
168
194
|
let retryAfter = 0;
|
|
169
195
|
if (response.status === 200) {
|
|
170
|
-
receipt = parseReceipt(await readJson(response), traceId, expectedIds, url.origin);
|
|
171
|
-
if (!
|
|
196
|
+
receipt = parseReceipt(await readJson(response), traceId, expectedIds, url.origin, setup);
|
|
197
|
+
if (!requestSignal.aborted &&
|
|
172
198
|
performance.now() < deadline &&
|
|
173
199
|
receipt.missingSpanIds.length === 0 &&
|
|
174
200
|
requiredFields.every((field) => receipt.fields[field]))
|
|
@@ -195,7 +221,7 @@ export async function verifyTrace(connection, traceId, options = {}) {
|
|
|
195
221
|
if (remaining <= 0)
|
|
196
222
|
break;
|
|
197
223
|
const wait = Math.max(delay, retryAfter);
|
|
198
|
-
await pause(Math.min(wait, remaining),
|
|
224
|
+
await pause(Math.min(wait, remaining), requestSignal);
|
|
199
225
|
// A truncated backoff exhausts this call even if a timer wakes just early.
|
|
200
226
|
if (wait >= remaining)
|
|
201
227
|
break;
|
|
@@ -203,6 +229,8 @@ export async function verifyTrace(connection, traceId, options = {}) {
|
|
|
203
229
|
}
|
|
204
230
|
}
|
|
205
231
|
catch (error) {
|
|
232
|
+
if (signal?.aborted)
|
|
233
|
+
throw new Error("Setup interrupted");
|
|
206
234
|
if (!controller.signal.aborted && performance.now() < deadline) {
|
|
207
235
|
if (error instanceof HueTraceVerificationError)
|
|
208
236
|
throw error;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { SetupFileChange } from "./configure.js";
|
|
2
|
+
import { type FileSetupInstallationStore, type SetupInstallationRecord, type SetupStoredApplicationEvidence } from "./installation.js";
|
|
3
|
+
import type { SetupProjectDetection } from "./types.js";
|
|
4
|
+
/** Closed automatic application matrix; other projects require an explicit agent-owned integration. */
|
|
5
|
+
export type SetupApplicationPlan = {
|
|
6
|
+
/** JavaScript or TypeScript application using the shared TypeScript SDK. */
|
|
7
|
+
language: "typescript";
|
|
8
|
+
/** Package manager selected from the project's manifest and lockfile. */
|
|
9
|
+
manager: "bun" | "npm";
|
|
10
|
+
/** Recognized HTTP framework. */
|
|
11
|
+
framework: "express";
|
|
12
|
+
/** Runtime named by the recognized start script; defaults to Node when omitted. */
|
|
13
|
+
runtime?: "node" | "bun";
|
|
14
|
+
/** Whether the start script explicitly enables Node source maps. */
|
|
15
|
+
sourceMaps?: boolean;
|
|
16
|
+
/** Validated project-relative application entrypoint. */
|
|
17
|
+
entrypoint: string;
|
|
18
|
+
/** One literal loopback HTTP pathname selected for the application request. */
|
|
19
|
+
requestPath: string;
|
|
20
|
+
/** SHA-256 of the source inspected during planning. */
|
|
21
|
+
entryDigest: string;
|
|
22
|
+
} | {
|
|
23
|
+
/** Python application using the Python SDK. */
|
|
24
|
+
language: "python";
|
|
25
|
+
/** Environment manager for the supported Python application. */
|
|
26
|
+
manager: "uv";
|
|
27
|
+
/** Recognized HTTP framework. */
|
|
28
|
+
framework: "flask";
|
|
29
|
+
/** Supported project-relative Flask entrypoint. */
|
|
30
|
+
entrypoint: "app.py";
|
|
31
|
+
/** One literal loopback HTTP pathname selected for the application request. */
|
|
32
|
+
requestPath: string;
|
|
33
|
+
/** SHA-256 of the source inspected during planning. */
|
|
34
|
+
entryDigest: string;
|
|
35
|
+
};
|
|
36
|
+
/** Stable reason an automatic application integration is not safe. */
|
|
37
|
+
export declare class SetupApplicationActionRequired extends Error {
|
|
38
|
+
/** Stable reason the caller must resolve before automatic integration continues. */
|
|
39
|
+
readonly code: "ambiguous-project" | "unsupported-manager" | "unsupported-framework" | "ambiguous-entrypoint" | "custom-instrumentation";
|
|
40
|
+
constructor(
|
|
41
|
+
/** Stable reason the caller must resolve before automatic integration continues. */
|
|
42
|
+
code: "ambiguous-project" | "unsupported-manager" | "unsupported-framework" | "ambiguous-entrypoint" | "custom-instrumentation", message: string);
|
|
43
|
+
}
|
|
44
|
+
/** Fixed argv execution boundary used for package managers and application entrypoints. */
|
|
45
|
+
export interface SetupCommand {
|
|
46
|
+
/** Fixed executable name or runtime path; never evaluated by a shell. */
|
|
47
|
+
command: string;
|
|
48
|
+
/** Explicit argument vector supplied to the executable. */
|
|
49
|
+
args: string[];
|
|
50
|
+
/** Selected application's working directory. */
|
|
51
|
+
cwd: string;
|
|
52
|
+
/** Optional child environment; values are never included in command output. */
|
|
53
|
+
env?: NodeJS.ProcessEnv;
|
|
54
|
+
/** Maximum elapsed execution time before termination. */
|
|
55
|
+
timeoutMillis: number;
|
|
56
|
+
}
|
|
57
|
+
/** Executes a fixed argv without a shell and returns only its status. */
|
|
58
|
+
export type SetupCommandRunner = (command: SetupCommand) => Promise<void>;
|
|
59
|
+
/** Statically recognizes the deliberately narrow automatic matrix without executing project code. */
|
|
60
|
+
export declare function planSetupApplication(project: SetupProjectDetection): Promise<SetupApplicationPlan>;
|
|
61
|
+
/** Default command runner: fixed argv, no shell, bounded output and deadline. */
|
|
62
|
+
export declare function runSetupCommand(input: SetupCommand): Promise<void>;
|
|
63
|
+
/** Installs the exact runtime with the detected owner of the project's manifest and lockfile. */
|
|
64
|
+
export declare function installSetupRuntime(project: SetupProjectDetection, plan: SetupApplicationPlan, runner?: SetupCommandRunner): Promise<boolean>;
|
|
65
|
+
/** Adds only two owned wiring blocks around an existing app object; business logic is untouched. */
|
|
66
|
+
export declare function wireSetupApplication(store: FileSetupInstallationStore, record: SetupInstallationRecord, plan: SetupApplicationPlan): Promise<SetupFileChange | undefined>;
|
|
67
|
+
/** Starts the existing entrypoint without a shell and exercises one existing HTTP GET route. */
|
|
68
|
+
export declare function exerciseSetupApplication(store: FileSetupInstallationStore, record: SetupInstallationRecord, plan: SetupApplicationPlan, signal?: AbortSignal, deadlines?: {
|
|
69
|
+
readinessMillis: number;
|
|
70
|
+
requestMillis: number;
|
|
71
|
+
evidenceMillis: number;
|
|
72
|
+
/** Internal loopback test seam; the public CLI always chooses a random port. */
|
|
73
|
+
port?: number;
|
|
74
|
+
}): Promise<SetupStoredApplicationEvidence>;
|