@stigmer/server 3.22.1 → 3.23.0

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 (71) hide show
  1. package/dist/artifactstorage/artifact-storage.d.ts +29 -31
  2. package/dist/artifactstorage/artifact-storage.d.ts.map +1 -1
  3. package/dist/artifactstorage/artifact-storage.js +5 -7
  4. package/dist/artifactstorage/artifact-storage.js.map +1 -1
  5. package/dist/artifactstorage/r2-storage.d.ts +12 -7
  6. package/dist/artifactstorage/r2-storage.d.ts.map +1 -1
  7. package/dist/artifactstorage/r2-storage.js +6 -14
  8. package/dist/artifactstorage/r2-storage.js.map +1 -1
  9. package/dist/boot/compose.d.ts.map +1 -1
  10. package/dist/boot/compose.js +49 -24
  11. package/dist/boot/compose.js.map +1 -1
  12. package/dist/boot/config.d.ts +4 -3
  13. package/dist/boot/config.d.ts.map +1 -1
  14. package/dist/boot/config.js.map +1 -1
  15. package/dist/boot/skill-transfer-origin.d.ts +52 -0
  16. package/dist/boot/skill-transfer-origin.d.ts.map +1 -0
  17. package/dist/boot/skill-transfer-origin.js +54 -0
  18. package/dist/boot/skill-transfer-origin.js.map +1 -0
  19. package/dist/domain/plugin/controller.d.ts +3 -7
  20. package/dist/domain/plugin/controller.d.ts.map +1 -1
  21. package/dist/domain/plugin/controller.js +8 -8
  22. package/dist/domain/plugin/controller.js.map +1 -1
  23. package/dist/domain/skill/constants.d.ts +26 -3
  24. package/dist/domain/skill/constants.d.ts.map +1 -1
  25. package/dist/domain/skill/constants.js +26 -3
  26. package/dist/domain/skill/constants.js.map +1 -1
  27. package/dist/domain/skill/controller.d.ts +6 -11
  28. package/dist/domain/skill/controller.d.ts.map +1 -1
  29. package/dist/domain/skill/controller.js +33 -22
  30. package/dist/domain/skill/controller.js.map +1 -1
  31. package/dist/domain/skill/push.d.ts +2 -2
  32. package/dist/domain/skill/push.d.ts.map +1 -1
  33. package/dist/domain/skill/push.js +2 -2
  34. package/dist/domain/skill/push.js.map +1 -1
  35. package/dist/domain/skill/transfer/handler.d.ts +17 -9
  36. package/dist/domain/skill/transfer/handler.d.ts.map +1 -1
  37. package/dist/domain/skill/transfer/handler.js +19 -8
  38. package/dist/domain/skill/transfer/handler.js.map +1 -1
  39. package/dist/domain/skill/transfer/slots.d.ts +26 -41
  40. package/dist/domain/skill/transfer/slots.d.ts.map +1 -1
  41. package/dist/domain/skill/transfer/slots.js +80 -87
  42. package/dist/domain/skill/transfer/slots.js.map +1 -1
  43. package/dist/domain/skill/transfer/staging.d.ts +48 -0
  44. package/dist/domain/skill/transfer/staging.d.ts.map +1 -0
  45. package/dist/domain/skill/transfer/staging.js +127 -0
  46. package/dist/domain/skill/transfer/staging.js.map +1 -0
  47. package/dist/pipeline/steps/resolve-artifact-source.d.ts +11 -8
  48. package/dist/pipeline/steps/resolve-artifact-source.d.ts.map +1 -1
  49. package/dist/pipeline/steps/resolve-artifact-source.js +8 -4
  50. package/dist/pipeline/steps/resolve-artifact-source.js.map +1 -1
  51. package/package.json +6 -6
  52. package/src/artifactstorage/__tests__/artifact-storage.test.ts +36 -33
  53. package/src/artifactstorage/__tests__/r2-storage.test.ts +11 -13
  54. package/src/artifactstorage/artifact-storage.ts +37 -33
  55. package/src/artifactstorage/r2-storage.ts +6 -15
  56. package/src/boot/__tests__/skill-transfer-origin.test.ts +72 -0
  57. package/src/boot/compose.ts +62 -25
  58. package/src/boot/config.ts +4 -3
  59. package/src/boot/skill-transfer-origin.ts +76 -0
  60. package/src/domain/plugin/controller.ts +15 -17
  61. package/src/domain/skill/__tests__/skill.test.ts +5 -3
  62. package/src/domain/skill/__tests__/slots.test.ts +113 -60
  63. package/src/domain/skill/__tests__/staging.test.ts +304 -0
  64. package/src/domain/skill/constants.ts +28 -3
  65. package/src/domain/skill/controller.ts +44 -35
  66. package/src/domain/skill/push.ts +3 -3
  67. package/src/domain/skill/transfer/handler.ts +33 -15
  68. package/src/domain/skill/transfer/slots.ts +89 -100
  69. package/src/domain/skill/transfer/staging.ts +191 -0
  70. package/src/pipeline/steps/resolve-artifact-source.ts +20 -13
  71. package/src/transport/registry/__tests__/lanes.test.ts +1 -1
@@ -7,72 +7,57 @@ export declare class SlotUnknownError extends Error {
7
7
  export declare class SlotConsumedError extends Error {
8
8
  constructor();
9
9
  }
10
- /** Go errSlotEmpty — minted but never (successfully) uploaded to. */
11
- export declare class SlotEmptyError extends Error {
12
- constructor();
13
- }
14
- /** Go errSizeMismatch — the body disagreed with the minted declaration. */
10
+ /** Go errSizeMismatch — the body disagreed with the reserved declaration. */
15
11
  export declare class SizeMismatchError extends Error {
16
12
  constructor(received: number, declared: number);
17
13
  }
18
14
  export declare class UploadSlots {
19
- private readonly slots;
20
- private readonly stagingDir;
15
+ private readonly root;
16
+ private readonly stagingPrefix;
21
17
  private readonly ttlMs;
22
18
  private readonly maxSize;
19
+ private readonly slots;
20
+ private readonly stagingDir;
23
21
  /** Injectable for expiry tests, mirroring Go's `now` field. */
24
22
  private readonly now;
25
23
  /**
26
- * Creates the registry and prepares the staging directory. Any file
27
- * already present is an orphan from a previous process (the registry
28
- * that knew about it died with that process), so the directory is
29
- * emptied — this is also the crash-recovery story for uploads that
30
- * never reached their push.
24
+ * Creates the registry over `root` (the local blob driver's root) and
25
+ * prepares the staging directory `root/stagingPrefix`. Any file already
26
+ * present is an orphan from a previous process (the registry that knew
27
+ * about it died with that process), so the directory is emptied — this
28
+ * is also the crash-recovery story for uploads that never reached their
29
+ * push.
31
30
  */
32
- constructor(stagingDir: string, ttlMs: number, maxSize: number, now?: () => number);
31
+ constructor(root: string, stagingPrefix: string, ttlMs: number, maxSize: number, now?: () => number);
33
32
  /**
34
- * Reserves an upload slot for an artifact of declaredSize bytes and
35
- * returns its single-use reference + TTL. The caller has already
33
+ * Reserves an upload slot for an artifact of declaredSize bytes at the
34
+ * staging key; returns the TTL granted. The caller has already
36
35
  * authorized the request and validated declaredSize against the skill
37
- * size limit; this guard is the registry's own invariant.
36
+ * size limit; the bound here is the registry's own invariant.
38
37
  */
39
- mint(declaredSize: number): {
40
- ref: string;
38
+ reserve(key: string, declaredSize: number): {
41
39
  ttlMs: number;
42
40
  };
43
41
  /**
44
42
  * Streams an upload's body into the slot's staging file. The body must
45
- * match the size declared at mint time exactly: a shorter body means a
43
+ * match the size declared at reservation exactly: a shorter body means a
46
44
  * truncated transfer, a longer one means the client lied — both reject
47
45
  * rather than staging bytes that would fail (or worse, surprise)
48
- * validation later. The staged file only becomes consumable once this
49
- * resolves.
50
- */
51
- receive(ref: string, body: Readable): Promise<void>;
52
- /**
53
- * Returns the staged bytes for ref and retires the slot — an upload
54
- * reference is strictly single-use. Push calls this when it sees
55
- * artifact_upload_ref; whatever happens downstream (validation failure
56
- * included), the slot is gone and the client must re-mint to retry.
46
+ * validation later. The staged file only becomes readable through the
47
+ * driver once this resolves.
57
48
  */
58
- consume(ref: string): Promise<Uint8Array>;
49
+ receive(key: string, body: Readable): Promise<void>;
59
50
  /**
60
- * Drops expired slots and their staged files. Called from mint, which
61
- * bounds the registry: it can hold at most the slots minted within one
51
+ * Drops expired slots and their staged files. Called from reserve, which
52
+ * bounds the registry: it can hold at most the slots reserved within one
62
53
  * TTL window.
63
54
  */
64
55
  private sweep;
65
56
  /**
66
- * The staging file name for a reference. Public since O5: the local
67
- * driver's presigned-PUT arm maps refs onto driver staging keys
68
- * (boot/compose.ts), and that mapping must come from HERE — a
69
- * re-declared "<ref>.zip" in the composition would drift from stagePath
70
- * with nothing to catch it.
71
- */
72
- stagedFileName(ref: string): string;
73
- /**
74
- * Maps a reference to its staging file. refs are server-generated hex
75
- * (never client-supplied paths), so simple joining is safe.
57
+ * Maps a staging key to its file: the key resolved under the driver's
58
+ * root, which is how the driver's download(key) will look for it. The
59
+ * key must carry the staging prefix and a plain hex file name — anything
60
+ * else is a caller bug, refused before it touches the filesystem.
76
61
  */
77
62
  private stagePath;
78
63
  }
@@ -1 +1 @@
1
- {"version":3,"file":"slots.d.ts","sourceRoot":"","sources":["../../../../src/domain/skill/transfer/slots.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAI5C,4DAA4D;AAC5D,qBAAa,gBAAiB,SAAQ,KAAK;;CAK1C;AAED,+DAA+D;AAC/D,qBAAa,iBAAkB,SAAQ,KAAK;;CAK3C;AAED,qEAAqE;AACrE,qBAAa,cAAe,SAAQ,KAAK;;CAKxC;AAED,2EAA2E;AAC3E,qBAAa,iBAAkB,SAAQ,KAAK;gBAC9B,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;CAI/C;AAQD,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA2B;IACjD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,+DAA+D;IAC/D,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IAEnC;;;;;;OAMG;gBAED,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,GAAG,GAAE,MAAM,MAAiB;IAU9B;;;;;OAKG;IACH,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE;IAc1D;;;;;;;OAOG;IACG,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IA6CzD;;;;;OAKG;IACG,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAuB/C;;;;OAIG;IACH,OAAO,CAAC,KAAK;IAUb;;;;;;OAMG;IACH,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAInC;;;OAGG;IACH,OAAO,CAAC,SAAS;CAGlB"}
1
+ {"version":3,"file":"slots.d.ts","sourceRoot":"","sources":["../../../../src/domain/skill/transfer/slots.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,4DAA4D;AAC5D,qBAAa,gBAAiB,SAAQ,KAAK;;CAK1C;AAED,+DAA+D;AAC/D,qBAAa,iBAAkB,SAAQ,KAAK;;CAK3C;AAED,6EAA6E;AAC7E,qBAAa,iBAAkB,SAAQ,KAAK;gBAC9B,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;CAM/C;AAgBD,qBAAa,WAAW;IAepB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAjB1B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA2B;IACjD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,+DAA+D;IAC/D,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IAEnC;;;;;;;OAOG;gBAEgB,IAAI,EAAE,MAAM,EACZ,aAAa,EAAE,MAAM,EACrB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EAChC,GAAG,GAAE,MAAM,MAAiB;IAQ9B;;;;;OAKG;IACH,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE;IAmB7D;;;;;;;OAOG;IACG,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAoDzD;;;;OAIG;IACH,OAAO,CAAC,KAAK;IAUb;;;;;OAKG;IACH,OAAO,CAAC,SAAS;CAUlB"}
@@ -1,9 +1,18 @@
1
1
  /**
2
- * Skill artifact upload slots — ports pkg/domain/skill/transfer/slots.go.
3
- * The in-memory registry of single-use upload capabilities plus the
4
- * staging directory their bytes land in (#675).
2
+ * Skill artifact upload slots — the local blob driver's staging mechanism,
3
+ * ported from pkg/domain/skill/transfer/slots.go: an in-memory registry of
4
+ * single-use upload capabilities plus the staging files their bytes land
5
+ * in (#675).
5
6
  *
6
- * In-memory is deliberate: the OSS server is single-instance (the same
7
+ * A slot is keyed by the STAGING KEY the domain names (`skills/staging/
8
+ * <hex>.zip`, transfer/staging.ts), and its file is that key resolved
9
+ * under the blob driver's root — so the driver's own download(key) reads
10
+ * what the lane received and its delete(key) retires the bytes, exactly
11
+ * as a bucket driver's would. The registry therefore knows nothing of
12
+ * consumption: it reserves, receives, and sweeps; the domain reads and
13
+ * deletes through the driver, one path for every backend.
14
+ *
15
+ * In-memory is deliberate: the local driver is single-instance (the same
7
16
  * assumption the in-process router transport and SQLite store already
8
17
  * make), and a slot is worthless across restarts anyway — its bytes live
9
18
  * in the staging directory, which is swept on boot.
@@ -11,17 +20,14 @@
11
20
  * Sentinel error classes replace Go's sentinel error values so the HTTP
12
21
  * handler maps registry failures onto honest status codes without string
13
22
  * matching; their message texts are Go's, verbatim (they ride wire-visible
14
- * copy: push's "artifact_upload_ref not usable: %v" and the handler's 400
15
- * body).
23
+ * copy: the handler's 404, 409 and 400 bodies).
16
24
  *
17
25
  * Proven by __tests__/slots.test.ts (injected clock for expiry) and the
18
26
  * conformance suite's transfer-lane tests.
19
27
  */
20
- import { randomBytes } from "node:crypto";
21
28
  import fs from "node:fs";
22
29
  import path from "node:path";
23
30
  import { pipeline } from "node:stream/promises";
24
- import { REF_BYTE_LEN, REF_PREFIX } from "../constants.js";
25
31
  /** Go errSlotUnknown — never existed, expired, or swept. */
26
32
  export class SlotUnknownError extends Error {
27
33
  constructor() {
@@ -36,71 +42,79 @@ export class SlotConsumedError extends Error {
36
42
  this.name = "SlotConsumedError";
37
43
  }
38
44
  }
39
- /** Go errSlotEmpty — minted but never (successfully) uploaded to. */
40
- export class SlotEmptyError extends Error {
41
- constructor() {
42
- super("upload reference has no uploaded bytes");
43
- this.name = "SlotEmptyError";
44
- }
45
- }
46
- /** Go errSizeMismatch — the body disagreed with the minted declaration. */
45
+ /** Go errSizeMismatch — the body disagreed with the reserved declaration. */
47
46
  export class SizeMismatchError extends Error {
48
47
  constructor(received, declared) {
49
48
  super(`upload size mismatch: received ${received} bytes, declared ${declared}`);
50
49
  this.name = "SizeMismatchError";
51
50
  }
52
51
  }
52
+ /**
53
+ * The one file-name shape a staging key may carry past its prefix: the
54
+ * domain's hex reference plus the archive suffix. Keys are server-minted,
55
+ * never client-supplied, so this is an invariant check, not input
56
+ * validation — a violation is a programming error and throws.
57
+ */
58
+ const STAGED_FILE_NAME = /^[0-9a-f]+\.zip$/;
53
59
  export class UploadSlots {
54
- slots = new Map();
55
- stagingDir;
60
+ root;
61
+ stagingPrefix;
56
62
  ttlMs;
57
63
  maxSize;
64
+ slots = new Map();
65
+ stagingDir;
58
66
  /** Injectable for expiry tests, mirroring Go's `now` field. */
59
67
  now;
60
68
  /**
61
- * Creates the registry and prepares the staging directory. Any file
62
- * already present is an orphan from a previous process (the registry
63
- * that knew about it died with that process), so the directory is
64
- * emptied — this is also the crash-recovery story for uploads that
65
- * never reached their push.
69
+ * Creates the registry over `root` (the local blob driver's root) and
70
+ * prepares the staging directory `root/stagingPrefix`. Any file already
71
+ * present is an orphan from a previous process (the registry that knew
72
+ * about it died with that process), so the directory is emptied — this
73
+ * is also the crash-recovery story for uploads that never reached their
74
+ * push.
66
75
  */
67
- constructor(stagingDir, ttlMs, maxSize, now = Date.now) {
68
- fs.rmSync(stagingDir, { recursive: true, force: true });
69
- fs.mkdirSync(stagingDir, { recursive: true, mode: 0o700 });
70
- this.stagingDir = stagingDir;
76
+ constructor(root, stagingPrefix, ttlMs, maxSize, now = Date.now) {
77
+ this.root = root;
78
+ this.stagingPrefix = stagingPrefix;
71
79
  this.ttlMs = ttlMs;
72
80
  this.maxSize = maxSize;
81
+ this.stagingDir = path.join(root, stagingPrefix);
82
+ fs.rmSync(this.stagingDir, { recursive: true, force: true });
83
+ fs.mkdirSync(this.stagingDir, { recursive: true, mode: 0o700 });
73
84
  this.now = now;
74
85
  }
75
86
  /**
76
- * Reserves an upload slot for an artifact of declaredSize bytes and
77
- * returns its single-use reference + TTL. The caller has already
87
+ * Reserves an upload slot for an artifact of declaredSize bytes at the
88
+ * staging key; returns the TTL granted. The caller has already
78
89
  * authorized the request and validated declaredSize against the skill
79
- * size limit; this guard is the registry's own invariant.
90
+ * size limit; the bound here is the registry's own invariant.
80
91
  */
81
- mint(declaredSize) {
92
+ reserve(key, declaredSize) {
82
93
  if (declaredSize <= 0 || declaredSize > this.maxSize) {
83
94
  throw new Error(`declared size ${declaredSize} outside (0, ${this.maxSize}]`);
84
95
  }
85
- const ref = REF_PREFIX + randomBytes(REF_BYTE_LEN).toString("hex");
96
+ this.stagePath(key);
97
+ if (this.slots.has(key)) {
98
+ throw new Error(`staging key ${key} is already reserved`);
99
+ }
86
100
  this.sweep();
87
- this.slots.set(ref, {
101
+ this.slots.set(key, {
88
102
  declaredSize,
89
103
  expiresAtMs: this.now() + this.ttlMs,
90
104
  uploaded: false,
91
105
  });
92
- return { ref, ttlMs: this.ttlMs };
106
+ return { ttlMs: this.ttlMs };
93
107
  }
94
108
  /**
95
109
  * Streams an upload's body into the slot's staging file. The body must
96
- * match the size declared at mint time exactly: a shorter body means a
110
+ * match the size declared at reservation exactly: a shorter body means a
97
111
  * truncated transfer, a longer one means the client lied — both reject
98
112
  * rather than staging bytes that would fail (or worse, surprise)
99
- * validation later. The staged file only becomes consumable once this
100
- * resolves.
113
+ * validation later. The staged file only becomes readable through the
114
+ * driver once this resolves.
101
115
  */
102
- async receive(ref, body) {
103
- const slot = this.slots.get(ref);
116
+ async receive(key, body) {
117
+ const slot = this.slots.get(key);
104
118
  if (slot === undefined || this.now() > slot.expiresAtMs) {
105
119
  throw new SlotUnknownError();
106
120
  }
@@ -108,9 +122,16 @@ export class UploadSlots {
108
122
  throw new SlotConsumedError();
109
123
  }
110
124
  const declared = slot.declaredSize;
111
- const filePath = this.stagePath(ref);
125
+ const filePath = this.stagePath(key);
112
126
  let written = 0;
113
127
  try {
128
+ // The driver's delete(key) prunes directories it empties, so the
129
+ // staging directory may be gone between two uploads; recreate it
130
+ // rather than fail the second one.
131
+ await fs.promises.mkdir(path.dirname(filePath), {
132
+ recursive: true,
133
+ mode: 0o700,
134
+ });
114
135
  // Consume at most declared+1 bytes: seeing the extra byte proves the
115
136
  // body exceeds the declaration without buffering an unbounded stream
116
137
  // (Go's io.LimitReader(declared+1) + written != declared check).
@@ -128,7 +149,7 @@ export class UploadSlots {
128
149
  }
129
150
  // Re-check after the write: the slot may have expired mid-upload
130
151
  // (Go re-checks under the lock for the same reason).
131
- const current = this.slots.get(ref);
152
+ const current = this.slots.get(key);
132
153
  if (current === undefined || this.now() > current.expiresAtMs) {
133
154
  await fs.promises.rm(filePath, { force: true });
134
155
  throw new SlotUnknownError();
@@ -136,62 +157,34 @@ export class UploadSlots {
136
157
  current.uploaded = true;
137
158
  }
138
159
  /**
139
- * Returns the staged bytes for ref and retires the slot — an upload
140
- * reference is strictly single-use. Push calls this when it sees
141
- * artifact_upload_ref; whatever happens downstream (validation failure
142
- * included), the slot is gone and the client must re-mint to retry.
143
- */
144
- async consume(ref) {
145
- const slot = this.slots.get(ref);
146
- if (slot === undefined || this.now() > slot.expiresAtMs) {
147
- throw new SlotUnknownError();
148
- }
149
- if (!slot.uploaded) {
150
- throw new SlotEmptyError();
151
- }
152
- this.slots.delete(ref);
153
- const filePath = this.stagePath(ref);
154
- try {
155
- const data = await fs.promises.readFile(filePath);
156
- return data;
157
- }
158
- catch (error) {
159
- throw new Error(`failed to read staged artifact: ${error instanceof Error ? error.message : String(error)}`);
160
- }
161
- finally {
162
- await fs.promises.rm(filePath, { force: true });
163
- }
164
- }
165
- /**
166
- * Drops expired slots and their staged files. Called from mint, which
167
- * bounds the registry: it can hold at most the slots minted within one
160
+ * Drops expired slots and their staged files. Called from reserve, which
161
+ * bounds the registry: it can hold at most the slots reserved within one
168
162
  * TTL window.
169
163
  */
170
164
  sweep() {
171
165
  const nowMs = this.now();
172
- for (const [ref, slot] of this.slots) {
166
+ for (const [key, slot] of this.slots) {
173
167
  if (nowMs > slot.expiresAtMs) {
174
- this.slots.delete(ref);
175
- fs.rmSync(this.stagePath(ref), { force: true });
168
+ this.slots.delete(key);
169
+ fs.rmSync(this.stagePath(key), { force: true });
176
170
  }
177
171
  }
178
172
  }
179
173
  /**
180
- * The staging file name for a reference. Public since O5: the local
181
- * driver's presigned-PUT arm maps refs onto driver staging keys
182
- * (boot/compose.ts), and that mapping must come from HERE — a
183
- * re-declared "<ref>.zip" in the composition would drift from stagePath
184
- * with nothing to catch it.
174
+ * Maps a staging key to its file: the key resolved under the driver's
175
+ * root, which is how the driver's download(key) will look for it. The
176
+ * key must carry the staging prefix and a plain hex file name — anything
177
+ * else is a caller bug, refused before it touches the filesystem.
185
178
  */
186
- stagedFileName(ref) {
187
- return `${ref}.zip`;
188
- }
189
- /**
190
- * Maps a reference to its staging file. refs are server-generated hex
191
- * (never client-supplied paths), so simple joining is safe.
192
- */
193
- stagePath(ref) {
194
- return path.join(this.stagingDir, this.stagedFileName(ref));
179
+ stagePath(key) {
180
+ if (!key.startsWith(this.stagingPrefix)) {
181
+ throw new Error(`staging key ${key} is outside ${this.stagingPrefix}`);
182
+ }
183
+ const fileName = key.slice(this.stagingPrefix.length);
184
+ if (!STAGED_FILE_NAME.test(fileName)) {
185
+ throw new Error(`staging key ${key} does not name a staged archive`);
186
+ }
187
+ return path.join(this.root, key);
195
188
  }
196
189
  }
197
190
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"slots.js","sourceRoot":"","sources":["../../../../src/domain/skill/transfer/slots.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"slots.js","sourceRoot":"","sources":["../../../../src/domain/skill/transfer/slots.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ import type { ArtifactStorage } from "../../../artifactstorage/artifact-storage.js";
2
+ import type { Logger } from "../../../boot/logger.js";
3
+ /** A fresh upload reference: the prefix and 128 random bits, hex. */
4
+ export declare function newUploadRef(): string;
5
+ /**
6
+ * The staging key a reference's bytes land under, or undefined when the
7
+ * reference is not one this server could have minted. Refs arrive from
8
+ * clients (the push request, the lane's URL path), so the shape is
9
+ * checked before it becomes a storage key.
10
+ */
11
+ export declare function stagingKeyOf(ref: string): string | undefined;
12
+ /**
13
+ * The reference for a staging key this module minted. Keys reach here from
14
+ * the domain's own mint, never from a client, so a foreign shape is a
15
+ * programming error and throws.
16
+ */
17
+ export declare function uploadRefOf(stagingKey: string): string;
18
+ /** What createArtifactUploadUrl hands the client. */
19
+ export interface StagedUpload {
20
+ /** The single-use reference the push presents. */
21
+ readonly ref: string;
22
+ /** Accepts one HTTP PUT of exactly the declared byte count. */
23
+ readonly url: string;
24
+ /** How long the reference and the URL stay valid. */
25
+ readonly ttlMs: number;
26
+ }
27
+ /** What getArtifactDownloadUrl hands the client. */
28
+ export interface DownloadCapability {
29
+ readonly url: string;
30
+ /** The floor of the URL's validity (DOWNLOAD_URL_TTL_MS). */
31
+ readonly ttlMs: number;
32
+ }
33
+ /** The staging port both archive kinds (skills, plugins) push through. */
34
+ export interface ArchiveStaging {
35
+ /** Mints a reference and the URL its bytes are PUT to. */
36
+ mint(declaredSizeBytes: number): Promise<StagedUpload>;
37
+ /**
38
+ * Reads the staged bytes for a reference and retires it. Throws
39
+ * SlotUnknownError when the driver holds nothing under the reference;
40
+ * any other failure is the driver's and propagates as an infrastructure
41
+ * fault.
42
+ */
43
+ consume(ref: string): Promise<Uint8Array>;
44
+ /** A time-limited download URL for a stored archive key. */
45
+ downloadUrl(storageKey: string): Promise<DownloadCapability>;
46
+ }
47
+ export declare function newArchiveStaging(driver: ArtifactStorage, logger: Logger): ArchiveStaging;
48
+ //# sourceMappingURL=staging.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"staging.d.ts","sourceRoot":"","sources":["../../../../src/domain/skill/transfer/staging.ts"],"names":[],"mappings":"AAgCA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8CAA8C,CAAC;AAEpF,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AA4BtD,qEAAqE;AACrE,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAK5D;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAMtD;AAED,qDAAqD;AACrD,MAAM,WAAW,YAAY;IAC3B,kDAAkD;IAClD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,+DAA+D;IAC/D,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,qDAAqD;IACrD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,oDAAoD;AACpD,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,6DAA6D;IAC7D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,0EAA0E;AAC1E,MAAM,WAAW,cAAc;IAC7B,0DAA0D;IAC1D,IAAI,CAAC,iBAAiB,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IACvD;;;;;OAKG;IACH,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAC1C,4DAA4D;IAC5D,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;CAC9D;AAED,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,eAAe,EACvB,MAAM,EAAE,MAAM,GACb,cAAc,CA8DhB"}
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Archive staging — how an artifact above the gRPC message cap travels,
3
+ * on every blob driver, through one code path (#675, stigmer#1219).
4
+ *
5
+ * The domain owns the two identities and the mapping between them: the
6
+ * wire reference a client is handed (`sau_<hex>`, the capability the
7
+ * push presents) and the staging key the bytes land under in the blob
8
+ * store (`skills/staging/<hex>.zip`, the surface a sweep targets). A
9
+ * driver is asked only to sign or stage a PUT for a key it is given, to
10
+ * read the key back, and to delete it — so the local driver rides the
11
+ * transfer lane's slot registry and a bucket driver signs a PUT straight
12
+ * to the bucket, and nothing above this module can tell which.
13
+ *
14
+ * Consume reads then deletes: the delete is what keeps a reference
15
+ * single-use on a bucket, whose signed URL is otherwise repeatable within
16
+ * its TTL. The delete is best effort and loud — the bytes are already in
17
+ * hand, a replay of the same reference is a content-addressed no-op, and
18
+ * the bucket lifecycle rule on the staging prefix sweeps what the delete
19
+ * missed. On the local driver the same delete retires the staged file the
20
+ * slot registry would otherwise sweep at expiry.
21
+ *
22
+ * A reference the driver cannot read — malformed, never uploaded,
23
+ * expired, already consumed — is one condition on every driver
24
+ * ("unknown or expired"): a bucket cannot distinguish them, so neither
25
+ * does the wire.
26
+ *
27
+ * Proven by __tests__/staging.test.ts over a fake driver and over the
28
+ * real local driver with its slot registry; the conformance suite's
29
+ * transfer-lane block pins the wire behaviour on both editions.
30
+ */
31
+ import { randomBytes } from "node:crypto";
32
+ import { ArtifactStorageNotFoundError } from "../../../artifactstorage/artifact-storage.js";
33
+ import { DEFAULT_SLOT_TTL_MS, DOWNLOAD_URL_TTL_MS, MAX_ZIP_SIZE, REF_BYTE_LEN, REF_PREFIX, STAGING_KEY_PREFIX, } from "../constants.js";
34
+ import { SlotUnknownError } from "./slots.js";
35
+ /** The hex the reference and the staging key share; 128 bits, lowercase. */
36
+ const HEX = new RegExp(`^[0-9a-f]{${REF_BYTE_LEN * 2}}$`);
37
+ const STAGED_ARCHIVE_SUFFIX = ".zip";
38
+ /** The shared hex of a well-formed reference or key, else undefined. */
39
+ function hexBetween(value, prefix, suffix) {
40
+ if (!value.startsWith(prefix) || !value.endsWith(suffix)) {
41
+ return undefined;
42
+ }
43
+ const hex = value.slice(prefix.length, value.length - suffix.length);
44
+ return HEX.test(hex) ? hex : undefined;
45
+ }
46
+ /** A fresh upload reference: the prefix and 128 random bits, hex. */
47
+ export function newUploadRef() {
48
+ return REF_PREFIX + randomBytes(REF_BYTE_LEN).toString("hex");
49
+ }
50
+ /**
51
+ * The staging key a reference's bytes land under, or undefined when the
52
+ * reference is not one this server could have minted. Refs arrive from
53
+ * clients (the push request, the lane's URL path), so the shape is
54
+ * checked before it becomes a storage key.
55
+ */
56
+ export function stagingKeyOf(ref) {
57
+ const hex = hexBetween(ref, REF_PREFIX, "");
58
+ return hex === undefined
59
+ ? undefined
60
+ : `${STAGING_KEY_PREFIX}${hex}${STAGED_ARCHIVE_SUFFIX}`;
61
+ }
62
+ /**
63
+ * The reference for a staging key this module minted. Keys reach here from
64
+ * the domain's own mint, never from a client, so a foreign shape is a
65
+ * programming error and throws.
66
+ */
67
+ export function uploadRefOf(stagingKey) {
68
+ const hex = hexBetween(stagingKey, STAGING_KEY_PREFIX, STAGED_ARCHIVE_SUFFIX);
69
+ if (hex === undefined) {
70
+ throw new Error(`${stagingKey} is not a staging key this domain minted`);
71
+ }
72
+ return REF_PREFIX + hex;
73
+ }
74
+ export function newArchiveStaging(driver, logger) {
75
+ return {
76
+ async mint(declaredSizeBytes) {
77
+ // The controllers refuse over-limit declarations with the limit in
78
+ // the message before reaching here; this is the module's own
79
+ // invariant, so no driver is ever asked to stage more than the
80
+ // archive ceiling.
81
+ if (declaredSizeBytes <= 0 || declaredSizeBytes > MAX_ZIP_SIZE) {
82
+ throw new Error(`declared size ${declaredSizeBytes} outside (0, ${MAX_ZIP_SIZE}]`);
83
+ }
84
+ const ref = newUploadRef();
85
+ const key = stagingKeyOf(ref);
86
+ if (key === undefined) {
87
+ throw new Error(`minted reference ${ref} has no staging key`);
88
+ }
89
+ const presigned = await driver.presignPut(key, declaredSizeBytes, DEFAULT_SLOT_TTL_MS);
90
+ return { ref, url: presigned.url, ttlMs: presigned.ttlMs };
91
+ },
92
+ async consume(ref) {
93
+ const key = stagingKeyOf(ref);
94
+ if (key === undefined) {
95
+ throw new SlotUnknownError();
96
+ }
97
+ let data;
98
+ try {
99
+ // The allocation is bounded by construction: a bucket accepts only
100
+ // the signed Content-Length, the slot registry only the reserved
101
+ // size, and both were checked against MAX_ZIP_SIZE at mint.
102
+ data = await driver.download(key);
103
+ }
104
+ catch (error) {
105
+ if (error instanceof ArtifactStorageNotFoundError) {
106
+ throw new SlotUnknownError();
107
+ }
108
+ throw error;
109
+ }
110
+ try {
111
+ await driver.delete(key);
112
+ }
113
+ catch (error) {
114
+ logger.error("failed to delete consumed staged artifact", {
115
+ stagingKey: key,
116
+ error: error instanceof Error ? error.message : String(error),
117
+ });
118
+ }
119
+ return data;
120
+ },
121
+ async downloadUrl(storageKey) {
122
+ const url = await driver.getSignedUrl(storageKey, DOWNLOAD_URL_TTL_MS, "");
123
+ return { url, ttlMs: DOWNLOAD_URL_TTL_MS };
124
+ },
125
+ };
126
+ }
127
+ //# sourceMappingURL=staging.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"staging.js","sourceRoot":"","sources":["../../../../src/domain/skill/transfer/staging.ts"],"names":[],"mappings":"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"}
@@ -8,17 +8,20 @@
8
8
  * are indistinguishable past this point.
9
9
  *
10
10
  * Skills carried this step alone (the Go ResolveArtifactSource port);
11
- * plugins are pushed the same two ways over the same upload slots (one
12
- * upload surface, as boot/compose.ts wires it), so the step lives
13
- * here and each kind names its lane-absent sentence and the request
14
- * fields it reads.
11
+ * plugins are pushed the same two ways over the same staging port (one
12
+ * upload surface, as boot/compose.ts wires it), so the step lives here
13
+ * and each kind names its lane-absent sentence and the request fields it
14
+ * reads. The staging port (domain/skill/transfer/staging.ts) hides which
15
+ * blob driver holds the bytes: the step never sees a slot or a bucket.
15
16
  *
16
17
  * The unusable-reference arm is INVALID_ARGUMENT, not a server fault: an
17
- * unknown, expired, already consumed, or minted-but-never-uploaded
18
- * reference all mean the client must re-mint and re-upload.
18
+ * unknown, expired, already consumed, or never-uploaded reference all mean
19
+ * the client must re-mint and re-upload. Any other failure reading the
20
+ * staged bytes is the driver's — an infrastructure fault the executor
21
+ * answers as a sanitized Internal, never dressed as the client's mistake.
19
22
  */
20
23
  import type { DescMessage, MessageShape } from "@bufbuild/protobuf";
21
- import type { UploadSlots } from "../../domain/skill/transfer/slots.js";
24
+ import type { ArchiveStaging } from "../../domain/skill/transfer/staging.js";
22
25
  import type { PipelineStep } from "../pipeline.js";
23
26
  /** Where the resolved bytes ride for every downstream step. */
24
27
  export declare const ARTIFACT_BYTES_KEY = "pushArtifactBytes";
@@ -31,5 +34,5 @@ export interface ArtifactSourceBinding<Desc extends DescMessage> {
31
34
  /** The FailedPrecondition copy when no lane is configured; each kind pins its own. */
32
35
  readonly laneNotConfigured: string;
33
36
  }
34
- export declare function newResolveArtifactSourceStep<Desc extends DescMessage>(slots: UploadSlots | undefined, binding: ArtifactSourceBinding<Desc>): PipelineStep<Desc>;
37
+ export declare function newResolveArtifactSourceStep<Desc extends DescMessage>(staging: ArchiveStaging | undefined, binding: ArtifactSourceBinding<Desc>): PipelineStep<Desc>;
35
38
  //# sourceMappingURL=resolve-artifact-source.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"resolve-artifact-source.d.ts","sourceRoot":"","sources":["../../../src/pipeline/steps/resolve-artifact-source.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEpE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC;AAExE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAGnD,+DAA+D;AAC/D,eAAO,MAAM,kBAAkB,sBAAsB,CAAC;AAEtD,MAAM,WAAW,qBAAqB,CAAC,IAAI,SAAS,WAAW;IAC7D,yEAAyE;IACzE,MAAM,CAAC,KAAK,EAAE,YAAY,CAAC,IAAI,CAAC,GAAG;QACjC,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC;QAC9B,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;KACpC,CAAC;IACF,sFAAsF;IACtF,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;CACpC;AAED,wBAAgB,4BAA4B,CAAC,IAAI,SAAS,WAAW,EACnE,KAAK,EAAE,WAAW,GAAG,SAAS,EAC9B,OAAO,EAAE,qBAAqB,CAAC,IAAI,CAAC,GACnC,YAAY,CAAC,IAAI,CAAC,CAuBpB"}
1
+ {"version":3,"file":"resolve-artifact-source.d.ts","sourceRoot":"","sources":["../../../src/pipeline/steps/resolve-artifact-source.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGpE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wCAAwC,CAAC;AAE7E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAGnD,+DAA+D;AAC/D,eAAO,MAAM,kBAAkB,sBAAsB,CAAC;AAEtD,MAAM,WAAW,qBAAqB,CAAC,IAAI,SAAS,WAAW;IAC7D,yEAAyE;IACzE,MAAM,CAAC,KAAK,EAAE,YAAY,CAAC,IAAI,CAAC,GAAG;QACjC,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC;QAC9B,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;KACpC,CAAC;IACF,sFAAsF;IACtF,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;CACpC;AAED,wBAAgB,4BAA4B,CAAC,IAAI,SAAS,WAAW,EACnE,OAAO,EAAE,cAAc,GAAG,SAAS,EACnC,OAAO,EAAE,qBAAqB,CAAC,IAAI,CAAC,GACnC,YAAY,CAAC,IAAI,CAAC,CA0BpB"}