@hydracept/sdk 0.3.1 → 0.3.3

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 CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.3 — 2026-08-26
4
+
5
+ - Public product shape: `run`, `context`, `doctor --fix`, `jobs recover`, `funding`
6
+ - `hydracept.run-result.v1` façade across CLI / SDK / MCP
7
+ - Server-authoritative `maxCost` admission; `FundingRequired` machine error
8
+
9
+ ## 0.3.2 — 2026-08-26
10
+
11
+ - Ship with the 0.3.2 public-install constellation (receipt `pricing.charge`, first-success smoke)
12
+
3
13
  ## 0.3.1 — 2026-08-26
4
14
 
5
15
  - Job downloads compare receipt SHA-256 as hex (`sha256:<hex>` prefix is stripped)
package/README.md CHANGED
@@ -68,7 +68,7 @@ const created = await client.panels.createPanelSession({
68
68
  console.log(created.sessionId, created.accessToken);
69
69
  ```
70
70
 
71
- Pair with [`@hydracept/elements`](../../packages/elements) to mount the hosted iframe — do not copy protocol code.
71
+ Pair with [`@hydracept/elements`](https://www.npmjs.com/package/@hydracept/elements) to mount the hosted iframe — do not copy protocol code.
72
72
 
73
73
  Activate at https://hydracept.com/start · Docs: https://docs.hydracept.com
74
74
 
package/dist/index.d.ts CHANGED
@@ -25,4 +25,6 @@ export declare function createHydraceptClient(options: HydraceptClientOptions):
25
25
  export { HydraceptWorkspace } from './workspace.js';
26
26
  export { createHydraceptJobRunner } from './jobs.js';
27
27
  export type { HydraceptJobRunner, HydraceptJobRunResult } from './jobs.js';
28
+ export type { HydraceptRunResult, HydraceptRunArtifact, HydraceptRunPricing, HydraceptRunOptions, } from './run-result.js';
29
+ export { pricingFromJob } from './run-result.js';
28
30
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAC;AACzD,OAAO,EAAuB,KAAK,aAAa,EAAE,MAAM,WAAW,CAAC;AACpE,OAAO,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAC;AACzD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAEzD,YAAY,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACpG,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAChD,YAAY,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAC;AACzD,YAAY,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAC;AACzD,YAAY,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AACvD,YAAY,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACtD,YAAY,EACV,2BAA2B,EAC3B,mBAAmB,EACnB,0BAA0B,EAC1B,mBAAmB,EACnB,uBAAuB,GACxB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAChD,YAAY,EACV,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,aAAa,CAAC;IACpB,OAAO,EAAE,UAAU,CAAC,OAAO,yBAAyB,CAAC,CAAC;IACtD,OAAO,EAAE,UAAU,CAAC,OAAO,yBAAyB,CAAC,CAAC;IACtD,MAAM,EAAE,UAAU,CAAC,OAAO,wBAAwB,CAAC,CAAC;CACrD,CAAC;AAEF,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,sBAAsB,GAAG,eAAe,CAQtF;AAED,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAC;AACrD,YAAY,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAC;AACzD,OAAO,EAAuB,KAAK,aAAa,EAAE,MAAM,WAAW,CAAC;AACpE,OAAO,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAC;AACzD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAEzD,YAAY,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACpG,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAChD,YAAY,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAC;AACzD,YAAY,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAC;AACzD,YAAY,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AACvD,YAAY,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACtD,YAAY,EACV,2BAA2B,EAC3B,mBAAmB,EACnB,0BAA0B,EAC1B,mBAAmB,EACnB,uBAAuB,GACxB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAChD,YAAY,EACV,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,aAAa,CAAC;IACpB,OAAO,EAAE,UAAU,CAAC,OAAO,yBAAyB,CAAC,CAAC;IACtD,OAAO,EAAE,UAAU,CAAC,OAAO,yBAAyB,CAAC,CAAC;IACtD,MAAM,EAAE,UAAU,CAAC,OAAO,wBAAwB,CAAC,CAAC;CACrD,CAAC;AAEF,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,sBAAsB,GAAG,eAAe,CAQtF;AAED,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAC;AACrD,YAAY,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAC3E,YAAY,EACV,kBAAkB,EAClB,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC"}
package/dist/index.js CHANGED
@@ -18,3 +18,4 @@ export function createHydraceptClient(options) {
18
18
  }
19
19
  export { HydraceptWorkspace } from './workspace.js';
20
20
  export { createHydraceptJobRunner } from './jobs.js';
21
+ export { pricingFromJob } from './run-result.js';
package/dist/jobs.d.ts CHANGED
@@ -14,6 +14,7 @@ export type HydraceptJobRunResult = {
14
14
  export type HydraceptJobRunner = {
15
15
  run(capabilityKey: string, body: Record<string, unknown>, options?: {
16
16
  downloadDir?: string;
17
+ outputRoot?: string;
17
18
  pollMs?: number;
18
19
  }): Promise<HydraceptJobRunResult>;
19
20
  };
@@ -1 +1 @@
1
- {"version":3,"file":"jobs.d.ts","sourceRoot":"","sources":["../src/jobs.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,MAAM,MAAM,mBAAmB,GAAG;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACxC,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAWF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,GAAG,CACD,aAAa,EAAE,MAAM,EACrB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAClD,OAAO,CAAC,qBAAqB,CAAC,CAAC;CACnC,CAAC;AAmBF,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,CAI9E;AAyBD,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,eAAe,EACvB,OAAO,EAAE,mBAAmB,GAC3B,kBAAkB,CA+DpB"}
1
+ {"version":3,"file":"jobs.d.ts","sourceRoot":"","sources":["../src/jobs.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,MAAM,MAAM,mBAAmB,GAAG;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACxC,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAWF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,GAAG,CACD,aAAa,EAAE,MAAM,EACrB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GACvE,OAAO,CAAC,qBAAqB,CAAC,CAAC;CACnC,CAAC;AAmBF,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,CAI9E;AAyBD,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,eAAe,EACvB,OAAO,EAAE,mBAAmB,GAC3B,kBAAkB,CAoEpB"}
package/dist/jobs.js CHANGED
@@ -71,14 +71,19 @@ export function createHydraceptJobRunner(client, binding) {
71
71
  const jobId = String(submitted.jobId || submitted.id || '');
72
72
  if (!jobId)
73
73
  throw new Error('No jobId in submit response');
74
- const deadline = Date.now() + (options.pollMs ?? 120_000);
74
+ const dest = options.downloadDir ||
75
+ (options.outputRoot ? join(options.outputRoot, '.hydracept', 'output', jobId) : undefined);
76
+ const pollMs = options.pollMs ?? 120_000;
77
+ const deadline = Number.isFinite(pollMs) ? Date.now() + pollMs : Number.POSITIVE_INFINITY;
75
78
  let job = submitted;
76
79
  let status = String(submitted.status || 'queued');
77
- while (Date.now() < deadline) {
80
+ while (true) {
78
81
  job = (await client.runtime.getJob(jobId));
79
82
  status = String(job.status || 'unknown');
80
83
  if (['succeeded', 'failed', 'canceled', 'cancelled'].includes(status))
81
84
  break;
85
+ if (Date.now() >= deadline)
86
+ break;
82
87
  await sleep(2000);
83
88
  }
84
89
  let receipt = null;
@@ -86,7 +91,7 @@ export function createHydraceptJobRunner(client, binding) {
86
91
  receipt = (await client.runtime.getJobReceipt(jobId));
87
92
  }
88
93
  const downloads = [];
89
- if (options.downloadDir && status === 'succeeded' && receipt) {
94
+ if (dest && status === 'succeeded' && receipt) {
90
95
  const artifacts = receipt.artifacts || [];
91
96
  for (const item of artifacts) {
92
97
  const artifactId = String(item.artifactId || item.id || '');
@@ -103,7 +108,7 @@ export function createHydraceptJobRunner(client, binding) {
103
108
  throw new Error(`SHA-256 mismatch for ${artifactId}`);
104
109
  }
105
110
  const filename = filenameForDownload(item, artifactId);
106
- const target = join(options.downloadDir, filename);
111
+ const target = join(dest, filename);
107
112
  mkdirSync(dirname(target), { recursive: true });
108
113
  writeFileSync(target, bytes);
109
114
  downloads.push(target);
@@ -0,0 +1,39 @@
1
+ export type HydraceptRunArtifact = {
2
+ artifactId: string;
3
+ mediaType: string;
4
+ sha256?: string | null;
5
+ byteLength?: number | null;
6
+ remoteRef?: string | null;
7
+ localPath?: string | null;
8
+ verified: boolean;
9
+ };
10
+ export type HydraceptRunPricing = {
11
+ estimatedCost: number | null;
12
+ actualCost: number | null;
13
+ currency: string;
14
+ };
15
+ /** hydracept.run-result.v1 — semantic contract shared with Python/CLI/MCP. */
16
+ export type HydraceptRunResult = {
17
+ schemaVersion: 'hydracept.run-result.v1';
18
+ capability: string;
19
+ jobId: string | null;
20
+ executionId: string | null;
21
+ status: string;
22
+ output?: unknown;
23
+ typedOutput?: unknown;
24
+ artifacts: HydraceptRunArtifact[];
25
+ pricing: HydraceptRunPricing;
26
+ receipt: Record<string, unknown> | null;
27
+ idempotencyKey: string | null;
28
+ error?: Record<string, unknown> | null;
29
+ diagnostics?: Record<string, unknown> | null;
30
+ };
31
+ export type HydraceptRunOptions = {
32
+ wait?: boolean;
33
+ timeoutMs?: number;
34
+ maxCostUsd?: number;
35
+ idempotencyKey?: string;
36
+ out?: string;
37
+ };
38
+ export declare function pricingFromJob(job: Record<string, unknown>, receipt: Record<string, unknown> | null | undefined): HydraceptRunPricing;
39
+ //# sourceMappingURL=run-result.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-result.d.ts","sourceRoot":"","sources":["../src/run-result.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,oBAAoB,GAAG;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,8EAA8E;AAC9E,MAAM,MAAM,kBAAkB,GAAG;IAC/B,aAAa,EAAE,yBAAyB,CAAC;IACzC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,EAAE,oBAAoB,EAAE,CAAC;IAClC,OAAO,EAAE,mBAAmB,CAAC;IAC7B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACxC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC9C,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAAC;AAQF,wBAAgB,cAAc,CAC5B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,GAClD,mBAAmB,CAqBrB"}
@@ -0,0 +1,32 @@
1
+ function asNumber(value) {
2
+ if (value == null || value === '')
3
+ return null;
4
+ const n = typeof value === 'number' ? value : Number(value);
5
+ return Number.isFinite(n) ? n : null;
6
+ }
7
+ export function pricingFromJob(job, receipt) {
8
+ let estimated = asNumber(job.estimatedCost);
9
+ let actual = asNumber(job.actualCost);
10
+ const status = String(job.status || '').toLowerCase();
11
+ if (receipt) {
12
+ const pricing = receipt.pricing || {};
13
+ const charge = (pricing.charge?.customerCharge ||
14
+ {});
15
+ const quote = (pricing.quote?.customerTotal ||
16
+ {});
17
+ if (estimated == null) {
18
+ const micros = asNumber(quote.amountMicros);
19
+ if (micros != null)
20
+ estimated = micros / 1_000_000;
21
+ }
22
+ const sealed = asNumber(charge.amountMicros);
23
+ if (sealed != null)
24
+ actual = sealed / 1_000_000;
25
+ else if (status !== 'succeeded')
26
+ actual = null;
27
+ }
28
+ else if (status !== 'succeeded' && status !== 'failed') {
29
+ actual = null;
30
+ }
31
+ return { estimatedCost: estimated, actualCost: actual, currency: 'USD' };
32
+ }
@@ -13,5 +13,13 @@ export declare class HydraceptWorkspace {
13
13
  readonly binding: HydraceptWorkspaceBinding;
14
14
  private constructor();
15
15
  static open(root?: string): HydraceptWorkspace;
16
+ run(capabilityKey: string, body: Record<string, unknown>, options?: {
17
+ wait?: boolean;
18
+ timeoutMs?: number;
19
+ maxCostUsd?: number;
20
+ idempotencyKey?: string;
21
+ out?: string;
22
+ }): Promise<import('./run-result.js').HydraceptRunResult>;
23
+ private ensureExecutionProject;
16
24
  }
17
25
  //# sourceMappingURL=workspace.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../src/workspace.ts"],"names":[],"mappings":"AAMA,OAAO,EAA4B,KAAK,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAC9E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,MAAM,MAAM,yBAAyB,GAAG;IACtC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,qBAAa,kBAAkB;IAC7B,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;IACjC,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAC;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,yBAAyB,CAAC;IAE5C,OAAO;IAYP,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,kBAAkB;CAqC/C"}
1
+ {"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../src/workspace.ts"],"names":[],"mappings":"AAMA,OAAO,EAA4B,KAAK,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAC9E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAGlD,MAAM,MAAM,yBAAyB,GAAG;IACtC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,qBAAa,kBAAkB;IAC7B,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;IACjC,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAC;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,yBAAyB,CAAC;IAE5C,OAAO;IAYP,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,kBAAkB;IAsCxC,GAAG,CACP,aAAa,EAAE,MAAM,EACrB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,GAAE;QACP,IAAI,CAAC,EAAE,OAAO,CAAC;QACf,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,GAAG,CAAC,EAAE,MAAM,CAAC;KACT,GACL,OAAO,CAAC,OAAO,iBAAiB,EAAE,kBAAkB,CAAC;YAgE1C,sBAAsB;CAsCrC"}
package/dist/workspace.js CHANGED
@@ -5,6 +5,7 @@ import { createHydraceptControlApi } from './control.js';
5
5
  import { createHydraceptPanelsApi } from './panels.js';
6
6
  import { createHydraceptRuntimeApi } from './runtime.js';
7
7
  import { createHydraceptJobRunner } from './jobs.js';
8
+ import { pricingFromJob } from './run-result.js';
8
9
  export class HydraceptWorkspace {
9
10
  client;
10
11
  jobs;
@@ -51,4 +52,91 @@ export class HydraceptWorkspace {
51
52
  const jobs = createHydraceptJobRunner(client, binding);
52
53
  return new HydraceptWorkspace(projectRoot, binding, client, jobs);
53
54
  }
55
+ async run(capabilityKey, body, options = {}) {
56
+ await this.ensureExecutionProject();
57
+ const { randomUUID } = await import('node:crypto');
58
+ const key = options.idempotencyKey || `run-${randomUUID().replace(/-/g, '')}`;
59
+ let payload = { ...body };
60
+ if (!('input' in payload)
61
+ && !('context' in payload)
62
+ && !('execution' in payload)
63
+ && !('idempotencyKey' in payload)) {
64
+ payload = { input: payload };
65
+ }
66
+ payload.idempotencyKey = key;
67
+ if (options.maxCostUsd != null) {
68
+ const existing = payload.execution || {};
69
+ const constraints = {
70
+ ...(existing.executionConstraints || {}),
71
+ maxCostUsd: options.maxCostUsd,
72
+ };
73
+ payload.execution = { ...existing, executionConstraints: constraints };
74
+ }
75
+ if (options.wait === false) {
76
+ const submitted = (await this.client.runtime.submitCapabilityJob(capabilityKey, payload));
77
+ const jobId = String(submitted.jobId || submitted.id || '');
78
+ if (!jobId)
79
+ throw new Error('No jobId in submit response');
80
+ return {
81
+ schemaVersion: 'hydracept.run-result.v1',
82
+ capability: capabilityKey,
83
+ jobId,
84
+ executionId: jobId,
85
+ status: 'running',
86
+ artifacts: [],
87
+ pricing: pricingFromJob(submitted, null),
88
+ receipt: null,
89
+ idempotencyKey: key,
90
+ };
91
+ }
92
+ const result = await this.jobs.run(capabilityKey, payload, {
93
+ downloadDir: options.out,
94
+ outputRoot: options.out ? undefined : this.root,
95
+ pollMs: options.timeoutMs ?? Number.POSITIVE_INFINITY,
96
+ });
97
+ return {
98
+ schemaVersion: 'hydracept.run-result.v1',
99
+ capability: capabilityKey,
100
+ jobId: result.jobId,
101
+ executionId: result.jobId,
102
+ status: result.status,
103
+ artifacts: result.downloads.map((localPath) => ({
104
+ artifactId: '',
105
+ mediaType: 'application/octet-stream',
106
+ localPath,
107
+ verified: true,
108
+ })),
109
+ pricing: pricingFromJob(result.job, result.receipt),
110
+ receipt: result.receipt,
111
+ idempotencyKey: key,
112
+ };
113
+ }
114
+ async ensureExecutionProject() {
115
+ let token = '';
116
+ try {
117
+ const diag = (await this.client.http.fetchJson('/diagnostics/session'));
118
+ token = String(diag.tokenProjectId || diag.principalProjectId || diag.projectId || '').trim();
119
+ }
120
+ catch {
121
+ throw new Error('Cannot resolve an execution project — checkout and credential must match. Run python -m hydracept doctor --fix');
122
+ }
123
+ let home = '';
124
+ try {
125
+ const session = (await this.client.http.fetchJson('/session/context'));
126
+ home = String(session.project?.id || '').trim();
127
+ }
128
+ catch {
129
+ home = '';
130
+ }
131
+ const checkout = this.binding.projectId;
132
+ if (token && home && token === home && token !== checkout) {
133
+ token = '';
134
+ }
135
+ if (!token) {
136
+ throw new Error('Cannot resolve an execution project — checkout and credential must match. Run python -m hydracept doctor --fix');
137
+ }
138
+ if (token !== checkout) {
139
+ throw new Error(`Checkout project ${checkout} disagrees with credential project ${token}. Hydracept will not manufacture an execution project.`);
140
+ }
141
+ }
54
142
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hydracept/sdk",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "description": "TypeScript client for Hydracept — production-ready game assets, with a receipt for every run.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",