@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.
Files changed (45) hide show
  1. package/CLI.md +270 -47
  2. package/ENVIRONMENTS.md +10 -0
  3. package/EVALUATIONS.md +1 -1
  4. package/README.md +20 -4
  5. package/dist/client.d.ts +5 -5
  6. package/dist/client.js +13 -6
  7. package/dist/environment/tools.d.ts +6 -1
  8. package/dist/environment/tools.js +7 -1
  9. package/dist/environment/types.d.ts +6 -1
  10. package/dist/evals/environment-target.js +1 -1
  11. package/dist/receipt.js +36 -8
  12. package/dist/setup/application.d.ts +74 -0
  13. package/dist/setup/application.js +766 -0
  14. package/dist/setup/backend.d.ts +229 -0
  15. package/dist/setup/backend.js +855 -0
  16. package/dist/setup/checkpoint.js +100 -30
  17. package/dist/setup/cli.js +20 -4
  18. package/dist/setup/configure.d.ts +13 -0
  19. package/dist/setup/configure.js +454 -0
  20. package/dist/setup/credential.d.ts +2 -0
  21. package/dist/setup/credential.js +9 -0
  22. package/dist/setup/detect.js +4 -1
  23. package/dist/setup/installation.d.ts +118 -0
  24. package/dist/setup/installation.js +605 -0
  25. package/dist/setup/lock.d.ts +2 -0
  26. package/dist/setup/lock.js +38 -0
  27. package/dist/setup/machine.d.ts +1 -10
  28. package/dist/setup/machine.js +8 -7
  29. package/dist/setup/render.d.ts +3 -1
  30. package/dist/setup/render.js +209 -6
  31. package/dist/setup/runner.d.ts +26 -76
  32. package/dist/setup/runner.js +320 -45
  33. package/dist/setup/socket.d.ts +7 -0
  34. package/dist/setup/socket.js +144 -0
  35. package/dist/setup/source.d.ts +9 -0
  36. package/dist/setup/source.js +269 -0
  37. package/dist/setup/types.d.ts +16 -9
  38. package/dist/setup/types.js +1 -1
  39. package/dist/setup.d.ts +6 -2
  40. package/dist/setup.js +3 -0
  41. package/dist/types.d.ts +24 -0
  42. package/dist/version.d.ts +1 -1
  43. package/dist/version.js +1 -1
  44. package/package.json +2 -1
  45. 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 (!controller.signal.aborted && performance.now() < deadline) {
161
- const response = await fetch(url, {
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: controller.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 (!controller.signal.aborted &&
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), controller.signal);
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>;