@deepraxis/sdk 0.1.0-dev.06cf3b8 → 0.1.0-dev.086e3bf

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.
@@ -1,5 +1,5 @@
1
1
  import type { Praxis } from '../core/client.js';
2
- import type { ApplicationAssetsConfig, ApplicationBinding } from '../generated/types.gen.js';
2
+ import type { ApplicationAssetsConfig, ApplicationBinding, ApplicationVersion } from '../generated/types.gen.js';
3
3
  export type AssetsConfig = ApplicationAssetsConfig;
4
4
  export interface PublishInput {
5
5
  /** 应用名;存在同名应用时复用。 */
@@ -20,16 +20,32 @@ export interface PublishInput {
20
20
  bindings?: ApplicationBinding[];
21
21
  /** 静态资源路由配置,原样传递。 */
22
22
  assets_config?: AssetsConfig;
23
+ /** Register an upload session to plan changes; do not upload content or publish. Requires an existing application. */
24
+ dryRun?: boolean;
25
+ /** Preserve current paths omitted from files. Defaults to false (whole-site replacement). */
26
+ keepExisting?: boolean;
23
27
  /** 是否等待部署完成,默认 true。 */
24
28
  wait?: boolean;
25
29
  /** 部署等待上限(毫秒),默认 120000。超时不会撤销部署。 */
26
30
  timeout_ms?: number;
27
31
  }
28
32
  export interface PublishResult {
33
+ /** True when a static-only publication matches the active version. */
34
+ unchanged?: boolean;
35
+ /** Current version when unchanged or planning an existing deployment. */
36
+ version?: ApplicationVersion;
37
+ /** dryRun compares against the active version; counts are distinct content hashes. */
38
+ plan?: {
39
+ added: string[];
40
+ changed: string[];
41
+ removed: string[];
42
+ upload_hashes: number;
43
+ existing_hashes: number;
44
+ };
29
45
  url: string;
30
46
  worker_id: string;
31
- version_id: string;
32
- deployment_id: string;
47
+ version_id: string | null;
48
+ deployment_id: string | null;
33
49
  status: string;
34
50
  created: boolean;
35
51
  /** 本次上传的不同内容 hash 数量。 */
@@ -37,5 +53,9 @@ export interface PublishResult {
37
53
  /** 服务端已存在的不同内容 hash 数量。 */
38
54
  reused: number;
39
55
  }
40
- /** Publish through the public SDK resources, including content-addressed incremental uploads. */
56
+ /** Publish with whole-site replacement by default; keepExisting preserves omitted paths.
57
+ * dryRun registers an idempotent upload session (stores its manifest), but uploads no content and does not publish.
58
+ * The platform does not support gradual rollout. Roll back using workers.deployments.create with
59
+ * strategy: 'percentage', versions: [{ version_id: previousVersionId, percentage: 100 }], while artifacts are retained.
60
+ */
41
61
  export declare function publish(client: Pick<Praxis, 'application'>, input: PublishInput): Promise<PublishResult>;
@@ -8,8 +8,16 @@ function base64(bytes) {
8
8
  }
9
9
  return btoa(chunks.join(''));
10
10
  }
11
- /** Publish through the public SDK resources, including content-addressed incremental uploads. */
11
+ /** Publish with whole-site replacement by default; keepExisting preserves omitted paths.
12
+ * dryRun registers an idempotent upload session (stores its manifest), but uploads no content and does not publish.
13
+ * The platform does not support gradual rollout. Roll back using workers.deployments.create with
14
+ * strategy: 'percentage', versions: [{ version_id: previousVersionId, percentage: 100 }], while artifacts are retained.
15
+ */
12
16
  export async function publish(client, input) {
17
+ const rolloutFields = ['gradual', 'gradualRollout', 'gradual_rollout', 'canary', 'rollout', 'percentage', 'percent', 'traffic', 'traffic_percentage', 'strategy', 'versions'];
18
+ if (Object.keys(input).some(key => rolloutFields.includes(key) || /percentage|percent|canary|gradual|rollout|灰度/i.test(key))) {
19
+ throw new PraxisError({ code: 'INVALID_PARAMS', message: '平台不支持灰度 (gradual rollout is not supported). Roll back with praxis.application.workers.deployments.create({ path: { worker_id }, body: { strategy: "percentage", versions: [{ version_id: previousVersionId, percentage: 100 }] } }); the previous version artifacts must still be retained.' });
20
+ }
13
21
  const timeout = input.timeout_ms ?? 120_000;
14
22
  if (!Number.isFinite(timeout) || timeout < 0)
15
23
  throw new RangeError('timeout_ms must be non-negative and finite');
@@ -21,7 +29,7 @@ export async function publish(client, input) {
21
29
  const files = Object.entries(input.files);
22
30
  if (files.length > 100_000)
23
31
  throw new PraxisError({ code: 'LIMIT_EXCEEDED', message: 'At most 100000 asset paths can be published' });
24
- const manifest = {};
32
+ let manifest = {};
25
33
  const contents = new Map();
26
34
  for (const [path, content] of files) {
27
35
  const normalized = path.startsWith('/') ? path : `/${path}`;
@@ -44,9 +52,51 @@ export async function publish(client, input) {
44
52
  }
45
53
  }
46
54
  const created = !worker;
55
+ if (!worker && input.dryRun)
56
+ throw new PraxisError({ code: 'NOT_FOUND', message: '请先创建应用 (create the application before dryRun)' });
47
57
  worker ??= await workers.create({ body: { name: input.name, subdomain: { enabled: true } } });
48
58
  const path = { worker_id: worker.id };
59
+ let currentVersion;
60
+ let currentDeployment;
61
+ let previous = {};
62
+ if (worker.praxis?.current_deployment_id) {
63
+ currentDeployment = await workers.deployments.retrieve({ path: { ...path, deployment_id: worker.praxis.current_deployment_id } });
64
+ const active = currentDeployment.versions[0];
65
+ if (!active)
66
+ throw new PraxisError({ code: 'INVALID_RESPONSE', message: 'Current deployment has no version' });
67
+ currentVersion = await workers.versions.retrieve({ path: { ...path, version_id: active.version_id } });
68
+ previous = (await workers.versions.assets.retrieveManifest({ path: { ...path, version_id: active.version_id } })).manifest;
69
+ }
70
+ // Keep the stored path order: the service hashes JSON.stringify(manifest), including property order.
71
+ // Reordered input with identical paths must still hash to the active version's manifest_hash.
72
+ manifest = {
73
+ ...Object.fromEntries(Object.entries(previous).filter(([key]) => input.keepExisting || Object.hasOwn(manifest, key))),
74
+ ...manifest,
75
+ };
76
+ if (Object.keys(manifest).length > 100_000)
77
+ throw new PraxisError({ code: 'LIMIT_EXCEEDED', message: 'At most 100000 asset paths can be published' });
78
+ const added = Object.keys(manifest).filter(key => !Object.hasOwn(previous, key));
79
+ const changed = Object.keys(manifest).filter(key => previous[key] && (previous[key].hash !== manifest[key].hash || previous[key].size !== manifest[key].size));
80
+ const removed = Object.keys(previous).filter(key => !Object.hasOwn(manifest, key));
81
+ const digest = new Uint8Array(await crypto.subtle.digest('SHA-256', new TextEncoder().encode(JSON.stringify(manifest))));
82
+ const manifestHash = Array.from(digest, byte => byte.toString(16).padStart(2, '0')).join('');
83
+ const staticOnly = !['main', 'bindings', 'compatibility_date', 'compatibility_flags', 'assets_config'].some(key => Object.hasOwn(input, key));
84
+ const unchanged = staticOnly && !!currentVersion && manifestHash === currentVersion.praxis.assets_manifest?.manifest_hash;
85
+ const hashes = new Set(Object.values(manifest).map(entry => entry.hash));
86
+ const currentResult = { url: worker.subdomain.url, worker_id: worker.id, version_id: currentVersion?.id ?? null,
87
+ deployment_id: currentDeployment?.id ?? null, status: currentDeployment?.praxis.status ?? 'draft', created,
88
+ ...(currentVersion ? { version: currentVersion } : {}), unchanged, uploaded: 0, reused: hashes.size };
89
+ if (unchanged && !input.dryRun)
90
+ return currentResult;
91
+ // dryRun intentionally registers the content-addressed session manifest, but never uploads or publishes.
49
92
  const session = await workers.assetsUploadSession.create({ path, body: { manifest } });
93
+ if (input.dryRun) {
94
+ const missing = new Set(session.buckets.flat());
95
+ if ([...missing].some(hash => !hashes.has(hash)))
96
+ throw new PraxisError({ code: 'INVALID_RESPONSE', message: 'Upload session requested an unknown hash' });
97
+ return { ...currentResult, reused: hashes.size - missing.size,
98
+ plan: { added, changed, removed, upload_hashes: missing.size, existing_hashes: hashes.size - missing.size } };
99
+ }
50
100
  let jwt = session.buckets.length ? null : session.jwt;
51
101
  const uploaded = new Set();
52
102
  for (const bucket of session.buckets) {
@@ -112,5 +162,5 @@ export async function publish(client, input) {
112
162
  }
113
163
  return { url: worker.subdomain.url, worker_id: worker.id, version_id: result.version.id,
114
164
  deployment_id: deployment.id, status: deployment.praxis.status, created,
115
- uploaded: uploaded.size, reused: contents.size - uploaded.size };
165
+ uploaded: uploaded.size, reused: hashes.size - uploaded.size };
116
166
  }
@@ -140,6 +140,12 @@ export declare const operations: readonly [{
140
140
  readonly type: "object";
141
141
  readonly required: false;
142
142
  readonly description: "Praxis extension: switchable model ids on /agent/v1 only. The default model must be included. When omitted, only the default model is selectable.";
143
+ }, {
144
+ readonly name: "preset";
145
+ readonly type: "string";
146
+ readonly enum: readonly ["minimal", "data-editor", "full"];
147
+ readonly required: false;
148
+ readonly description: "Optional tool preset. minimal enables read, glob, grep and ask_user_question; data-editor also enables write and edit; full uses the default toolset with always_allow. Explicit tools override matching preset entries. codemode requires bash and is absent from data-editor.";
143
149
  }];
144
150
  };
145
151
  }, {
@@ -268,6 +274,12 @@ export declare const operations: readonly [{
268
274
  readonly type: "object";
269
275
  readonly required: false;
270
276
  readonly description: "Praxis extension: switchable model ids on /agent/v1 only. The default model must be included. When omitted, only the default model is selectable.";
277
+ }, {
278
+ readonly name: "preset";
279
+ readonly type: "string";
280
+ readonly enum: readonly ["minimal", "data-editor", "full"];
281
+ readonly required: false;
282
+ readonly description: "Optional tool preset. minimal enables read, glob, grep and ask_user_question; data-editor also enables write and edit; full uses the default toolset with always_allow. Explicit tools override matching preset entries. codemode requires bash and is absent from data-editor.";
271
283
  }];
272
284
  };
273
285
  }, {
@@ -420,6 +432,20 @@ export declare const operations: readonly [{
420
432
  readonly sdkName: "external_user_id";
421
433
  }];
422
434
  readonly query: readonly [];
435
+ }, {
436
+ readonly resource: "agent.agents.unarchive";
437
+ readonly method: "create";
438
+ readonly summary: "Unarchive Agent";
439
+ readonly description: "Restore an archived agent without creating a configuration version. Already active agents are unchanged. Missing or inaccessible agents return 404. Use the returned agent id to create new sessions.";
440
+ readonly http: "POST";
441
+ readonly path: "/agent/v1/agents/{agent_id}/unarchive";
442
+ readonly streaming: "no";
443
+ readonly pagination: false;
444
+ readonly pathParams: readonly [{
445
+ readonly name: "agent_id";
446
+ readonly sdkName: "agent_id";
447
+ }];
448
+ readonly query: readonly [];
423
449
  }, {
424
450
  readonly resource: "agent.agents.versions";
425
451
  readonly method: "list";
@@ -582,7 +608,7 @@ export declare const operations: readonly [{
582
608
  readonly resource: "agent.computers";
583
609
  readonly method: "connect";
584
610
  readonly summary: "Connect computer";
585
- readonly description: "Resume the computer and issue a sandbox connection.\n\nOwn-face extension: Managed Agents has no persistent Computer connection. Returns the public SDK sandbox_id, envd_access_token and template_id, plus the machine tier the computer is on now (light or heavy) and the lease that keeps it there (lease_id, lease_expires_at; lease_id is null when no lease exists). Keep the lease while the connection stays open: renew it about every 20 seconds (POST .../leases/{lease_id}/renew) and release it on close (DELETE .../leases/{lease_id}). An unrenewed lease simply expires, and the full machine is reclaimed only after that. Archived computers remain accessible; foreign or absent computers return 404. Never cache credentials.";
611
+ readonly description: "Resume the computer and issue a sandbox connection.\n\nOwn-face extension: Managed Agents has no persistent Computer connection. Returns the public SDK sandbox_id, envd_access_token and template_id, plus the machine tier the computer is on now (light or heavy) and the lease that keeps it there (lease_id, lease_expires_at; lease_id is null when no lease exists). Keep the lease while the connection stays open: renew it about every 20 seconds (POST .../leases/{lease_id}/renew) and release it on close (DELETE .../leases/{lease_id}). An unrenewed lease simply expires, and the full machine is reclaimed only after that. Archived computers remain accessible; foreign or absent computers return 404. Never cache credentials.\n\nConnect to a computer with a caller-held lease. Omit the body or send {} to request light: the lightweight shell, or the heavy instance while it is running. Send {\"need\":\"heavy\"} to request full Linux for interactive programs; files are synchronized before the heavy connection is returned with tier heavy. Heavy running time counts toward the daily budget in seconds. Renew the lease while using the connection and release it with DELETE when finished. If the daily budget is exhausted, 429 COMPUTER_BUDGET_EXHAUSTED allows you to continue with need light.";
586
612
  readonly http: "POST";
587
613
  readonly path: "/agent/v1/computers/{computer_id}/connect";
588
614
  readonly streaming: "no";
@@ -592,6 +618,16 @@ export declare const operations: readonly [{
592
618
  readonly sdkName: "computer_id";
593
619
  }];
594
620
  readonly query: readonly [];
621
+ readonly body: {
622
+ readonly required: false;
623
+ readonly fields: readonly [{
624
+ readonly name: "need";
625
+ readonly type: "string";
626
+ readonly enum: readonly ["light", "heavy"];
627
+ readonly required: false;
628
+ readonly description: "Defaults to light. light uses the lightweight shell, or the heavy instance while it is running. heavy requests a full Linux computer for interactive programs; heavy running time counts toward the daily budget in seconds.";
629
+ }];
630
+ };
595
631
  }, {
596
632
  readonly resource: "agent.computers.files";
597
633
  readonly method: "list";
@@ -1485,6 +1521,20 @@ export declare const operations: readonly [{
1485
1521
  readonly description: "Praxis extension: explicit session thinking effort; null inherits the pinned agent default, then the model default. none disables thinking only when model.thinking.can_disable is true. Unsupported efforts return 400 invalid_request_error.";
1486
1522
  }];
1487
1523
  };
1524
+ }, {
1525
+ readonly resource: "agent.sessions";
1526
+ readonly method: "delete";
1527
+ readonly summary: "Delete session";
1528
+ readonly description: "Delete an idle or terminated session and its event history after saving a deletion snapshot. Supply the session id from creation or listing. Running sessions return 409 INVALID_STATE: interrupt first or wait until idle or terminated. Afterwards retrieval and event listing return 404 and lists omit the session. Example: DELETE /agent/v1/sessions/sesn_example.";
1529
+ readonly http: "DELETE";
1530
+ readonly path: "/agent/v1/sessions/{session_id}";
1531
+ readonly streaming: "no";
1532
+ readonly pagination: false;
1533
+ readonly pathParams: readonly [{
1534
+ readonly name: "session_id";
1535
+ readonly sdkName: "session_id";
1536
+ }];
1537
+ readonly query: readonly [];
1488
1538
  }, {
1489
1539
  readonly resource: "agent.sessions";
1490
1540
  readonly method: "retrieve";
@@ -2694,6 +2744,23 @@ export declare const operations: readonly [{
2694
2744
  readonly sdkName: "version_id";
2695
2745
  }];
2696
2746
  readonly query: readonly [];
2747
+ }, {
2748
+ readonly resource: "application.workers.versions.assets";
2749
+ readonly method: "retrieveManifest";
2750
+ readonly summary: "Get version asset manifest";
2751
+ readonly description: "Read the retained asset paths, content hashes and byte sizes of a version for publication planning. This read creates no session or version. A version without assets returns an empty manifest; unavailable artifacts and foreign resources return 404.";
2752
+ readonly http: "GET";
2753
+ readonly path: "/application/v1/workers/{worker_id}/versions/{version_id}/assets/manifest";
2754
+ readonly streaming: "no";
2755
+ readonly pagination: false;
2756
+ readonly pathParams: readonly [{
2757
+ readonly name: "worker_id";
2758
+ readonly sdkName: "worker_id";
2759
+ }, {
2760
+ readonly name: "version_id";
2761
+ readonly sdkName: "version_id";
2762
+ }];
2763
+ readonly query: readonly [];
2697
2764
  }, {
2698
2765
  readonly resource: "automation.actions";
2699
2766
  readonly method: "list";
@@ -175,6 +175,17 @@ export const operations = [
175
175
  "type": "object",
176
176
  "required": false,
177
177
  "description": "Praxis extension: switchable model ids on /agent/v1 only. The default model must be included. When omitted, only the default model is selectable."
178
+ },
179
+ {
180
+ "name": "preset",
181
+ "type": "string",
182
+ "enum": [
183
+ "minimal",
184
+ "data-editor",
185
+ "full"
186
+ ],
187
+ "required": false,
188
+ "description": "Optional tool preset. minimal enables read, glob, grep and ask_user_question; data-editor also enables write and edit; full uses the default toolset with always_allow. Explicit tools override matching preset entries. codemode requires bash and is absent from data-editor."
178
189
  }
179
190
  ]
180
191
  }
@@ -332,6 +343,17 @@ export const operations = [
332
343
  "type": "object",
333
344
  "required": false,
334
345
  "description": "Praxis extension: switchable model ids on /agent/v1 only. The default model must be included. When omitted, only the default model is selectable."
346
+ },
347
+ {
348
+ "name": "preset",
349
+ "type": "string",
350
+ "enum": [
351
+ "minimal",
352
+ "data-editor",
353
+ "full"
354
+ ],
355
+ "required": false,
356
+ "description": "Optional tool preset. minimal enables read, glob, grep and ask_user_question; data-editor also enables write and edit; full uses the default toolset with always_allow. Explicit tools override matching preset entries. codemode requires bash and is absent from data-editor."
335
357
  }
336
358
  ]
337
359
  }
@@ -522,6 +544,23 @@ export const operations = [
522
544
  ],
523
545
  "query": []
524
546
  },
547
+ {
548
+ "resource": "agent.agents.unarchive",
549
+ "method": "create",
550
+ "summary": "Unarchive Agent",
551
+ "description": "Restore an archived agent without creating a configuration version. Already active agents are unchanged. Missing or inaccessible agents return 404. Use the returned agent id to create new sessions.",
552
+ "http": "POST",
553
+ "path": "/agent/v1/agents/{agent_id}/unarchive",
554
+ "streaming": "no",
555
+ "pagination": false,
556
+ "pathParams": [
557
+ {
558
+ "name": "agent_id",
559
+ "sdkName": "agent_id"
560
+ }
561
+ ],
562
+ "query": []
563
+ },
525
564
  {
526
565
  "resource": "agent.agents.versions",
527
566
  "method": "list",
@@ -717,7 +756,7 @@ export const operations = [
717
756
  "resource": "agent.computers",
718
757
  "method": "connect",
719
758
  "summary": "Connect computer",
720
- "description": "Resume the computer and issue a sandbox connection.\n\nOwn-face extension: Managed Agents has no persistent Computer connection. Returns the public SDK sandbox_id, envd_access_token and template_id, plus the machine tier the computer is on now (light or heavy) and the lease that keeps it there (lease_id, lease_expires_at; lease_id is null when no lease exists). Keep the lease while the connection stays open: renew it about every 20 seconds (POST .../leases/{lease_id}/renew) and release it on close (DELETE .../leases/{lease_id}). An unrenewed lease simply expires, and the full machine is reclaimed only after that. Archived computers remain accessible; foreign or absent computers return 404. Never cache credentials.",
759
+ "description": "Resume the computer and issue a sandbox connection.\n\nOwn-face extension: Managed Agents has no persistent Computer connection. Returns the public SDK sandbox_id, envd_access_token and template_id, plus the machine tier the computer is on now (light or heavy) and the lease that keeps it there (lease_id, lease_expires_at; lease_id is null when no lease exists). Keep the lease while the connection stays open: renew it about every 20 seconds (POST .../leases/{lease_id}/renew) and release it on close (DELETE .../leases/{lease_id}). An unrenewed lease simply expires, and the full machine is reclaimed only after that. Archived computers remain accessible; foreign or absent computers return 404. Never cache credentials.\n\nConnect to a computer with a caller-held lease. Omit the body or send {} to request light: the lightweight shell, or the heavy instance while it is running. Send {\"need\":\"heavy\"} to request full Linux for interactive programs; files are synchronized before the heavy connection is returned with tier heavy. Heavy running time counts toward the daily budget in seconds. Renew the lease while using the connection and release it with DELETE when finished. If the daily budget is exhausted, 429 COMPUTER_BUDGET_EXHAUSTED allows you to continue with need light.",
721
760
  "http": "POST",
722
761
  "path": "/agent/v1/computers/{computer_id}/connect",
723
762
  "streaming": "no",
@@ -728,7 +767,22 @@ export const operations = [
728
767
  "sdkName": "computer_id"
729
768
  }
730
769
  ],
731
- "query": []
770
+ "query": [],
771
+ "body": {
772
+ "required": false,
773
+ "fields": [
774
+ {
775
+ "name": "need",
776
+ "type": "string",
777
+ "enum": [
778
+ "light",
779
+ "heavy"
780
+ ],
781
+ "required": false,
782
+ "description": "Defaults to light. light uses the lightweight shell, or the heavy instance while it is running. heavy requests a full Linux computer for interactive programs; heavy running time counts toward the daily budget in seconds."
783
+ }
784
+ ]
785
+ }
732
786
  },
733
787
  {
734
788
  "resource": "agent.computers.files",
@@ -1833,6 +1887,23 @@ export const operations = [
1833
1887
  ]
1834
1888
  }
1835
1889
  },
1890
+ {
1891
+ "resource": "agent.sessions",
1892
+ "method": "delete",
1893
+ "summary": "Delete session",
1894
+ "description": "Delete an idle or terminated session and its event history after saving a deletion snapshot. Supply the session id from creation or listing. Running sessions return 409 INVALID_STATE: interrupt first or wait until idle or terminated. Afterwards retrieval and event listing return 404 and lists omit the session. Example: DELETE /agent/v1/sessions/sesn_example.",
1895
+ "http": "DELETE",
1896
+ "path": "/agent/v1/sessions/{session_id}",
1897
+ "streaming": "no",
1898
+ "pagination": false,
1899
+ "pathParams": [
1900
+ {
1901
+ "name": "session_id",
1902
+ "sdkName": "session_id"
1903
+ }
1904
+ ],
1905
+ "query": []
1906
+ },
1836
1907
  {
1837
1908
  "resource": "agent.sessions",
1838
1909
  "method": "retrieve",
@@ -3326,6 +3397,27 @@ export const operations = [
3326
3397
  ],
3327
3398
  "query": []
3328
3399
  },
3400
+ {
3401
+ "resource": "application.workers.versions.assets",
3402
+ "method": "retrieveManifest",
3403
+ "summary": "Get version asset manifest",
3404
+ "description": "Read the retained asset paths, content hashes and byte sizes of a version for publication planning. This read creates no session or version. A version without assets returns an empty manifest; unavailable artifacts and foreign resources return 404.",
3405
+ "http": "GET",
3406
+ "path": "/application/v1/workers/{worker_id}/versions/{version_id}/assets/manifest",
3407
+ "streaming": "no",
3408
+ "pagination": false,
3409
+ "pathParams": [
3410
+ {
3411
+ "name": "worker_id",
3412
+ "sdkName": "worker_id"
3413
+ },
3414
+ {
3415
+ "name": "version_id",
3416
+ "sdkName": "version_id"
3417
+ }
3418
+ ],
3419
+ "query": []
3420
+ },
3329
3421
  {
3330
3422
  "resource": "automation.actions",
3331
3423
  "method": "list",