@shipfox/api-logs 11.0.0 → 12.0.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.
- package/.turbo/turbo-build.log +4 -4
- package/CHANGELOG.md +32 -0
- package/README.md +6 -6
- package/dist/config.js +1 -1
- package/dist/config.js.map +1 -1
- package/dist/core/append-logs.js +1 -1
- package/dist/core/append-logs.js.map +1 -1
- package/dist/core/budget.d.ts +1 -1
- package/dist/core/budget.js +1 -1
- package/dist/core/budget.js.map +1 -1
- package/dist/core/errors.d.ts +4 -4
- package/dist/core/errors.js +4 -4
- package/dist/core/errors.js.map +1 -1
- package/dist/db/schema/attempt-streams.d.ts +2 -2
- package/dist/db/schema/attempt-streams.js +2 -2
- package/dist/db/schema/attempt-streams.js.map +1 -1
- package/dist/db/schema/job-accounting.d.ts +1 -1
- package/dist/db/schema/job-accounting.js +1 -1
- package/dist/db/schema/job-accounting.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +9 -9
- package/src/config.ts +1 -1
- package/src/core/append-logs.test.ts +4 -0
- package/src/core/append-logs.ts +1 -1
- package/src/core/budget.ts +1 -1
- package/src/core/errors.ts +4 -4
- package/src/core/finalize-attempt-stream.test.ts +1 -0
- package/src/db/schema/attempt-streams.ts +2 -2
- package/src/db/schema/job-accounting.ts +1 -1
- package/test/fixtures/workflows-client.ts +1 -0
- package/test/globalSetup.ts +1 -1
- package/tsconfig.build.tsbuildinfo +1 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shipfox/api-logs",
|
|
3
3
|
"license": "MIT",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "12.0.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/ShipfoxHQ/shipfox.git",
|
|
@@ -25,18 +25,18 @@
|
|
|
25
25
|
"@temporalio/workflow": "1.18.1",
|
|
26
26
|
"drizzle-orm": "^0.45.2",
|
|
27
27
|
"zod": "^4.4.3",
|
|
28
|
-
"@shipfox/api-auth-context": "
|
|
29
|
-
"@shipfox/api-logs-dto": "
|
|
30
|
-
"@shipfox/
|
|
31
|
-
"@shipfox/
|
|
28
|
+
"@shipfox/api-auth-context": "12.0.0",
|
|
29
|
+
"@shipfox/api-logs-dto": "12.0.0",
|
|
30
|
+
"@shipfox/workflow-document": "3.0.0",
|
|
31
|
+
"@shipfox/api-workflows-dto": "12.0.0",
|
|
32
32
|
"@shipfox/config": "1.2.4",
|
|
33
|
-
"@shipfox/node-drizzle": "0.3.
|
|
33
|
+
"@shipfox/node-drizzle": "0.3.5",
|
|
34
34
|
"@shipfox/node-error-monitoring": "0.3.0",
|
|
35
|
-
"@shipfox/node-
|
|
36
|
-
"@shipfox/node-
|
|
35
|
+
"@shipfox/node-module": "1.0.5",
|
|
36
|
+
"@shipfox/node-fastify": "0.4.1",
|
|
37
37
|
"@shipfox/node-opentelemetry": "0.6.3",
|
|
38
38
|
"@shipfox/node-outbox": "0.2.6",
|
|
39
|
-
"@shipfox/node-postgres": "0.
|
|
39
|
+
"@shipfox/node-postgres": "0.5.0",
|
|
40
40
|
"@shipfox/node-temporal": "0.4.4"
|
|
41
41
|
},
|
|
42
42
|
"imports": {
|
package/src/config.ts
CHANGED
|
@@ -97,7 +97,7 @@ if (config.LOG_READ_INLINE_MAX_BYTES < 1) {
|
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
// A whole agent_session line is one record in one body. If the body limit is below the
|
|
100
|
-
// line cap, a legitimate large session line could never fit in one request body
|
|
100
|
+
// line cap, a legitimate large session line could never fit in one request body: Fastify
|
|
101
101
|
// would 413 it before the per-line validator runs. Fail fast at startup instead.
|
|
102
102
|
if (config.LOG_APPEND_BODY_LIMIT_BYTES < config.LOG_MAX_SESSION_LINE_BYTES) {
|
|
103
103
|
throw new Error(
|
|
@@ -193,6 +193,7 @@ describe('appendLogs', () => {
|
|
|
193
193
|
const workflows = createFakeInterModuleClients({
|
|
194
194
|
workflows: defineInterModulePresentation(workflowsInterModuleContract, {
|
|
195
195
|
startRunFromTrigger: vi.fn(),
|
|
196
|
+
resolveWorkflowRunTriggerReference: vi.fn(),
|
|
196
197
|
deliverEventToJobListener: vi.fn(),
|
|
197
198
|
getStepLogContext: () => ({harness: 'claude' as const}),
|
|
198
199
|
getLeasedAgentToolContext: vi.fn(),
|
|
@@ -238,6 +239,7 @@ describe('appendLogs', () => {
|
|
|
238
239
|
const workflows = createFakeInterModuleClients({
|
|
239
240
|
workflows: defineInterModulePresentation(workflowsInterModuleContract, {
|
|
240
241
|
startRunFromTrigger: vi.fn(),
|
|
242
|
+
resolveWorkflowRunTriggerReference: vi.fn(),
|
|
241
243
|
deliverEventToJobListener: vi.fn(),
|
|
242
244
|
getStepLogContext: () => ({harness: 'claude' as const}),
|
|
243
245
|
getLeasedAgentToolContext: vi.fn(),
|
|
@@ -318,6 +320,7 @@ describe('appendLogs', () => {
|
|
|
318
320
|
const workflows = createFakeInterModuleClients({
|
|
319
321
|
workflows: defineInterModulePresentation(workflowsInterModuleContract, {
|
|
320
322
|
startRunFromTrigger: vi.fn(),
|
|
323
|
+
resolveWorkflowRunTriggerReference: vi.fn(),
|
|
321
324
|
deliverEventToJobListener: vi.fn(),
|
|
322
325
|
getStepLogContext: () => ({harness: 'claude' as const}),
|
|
323
326
|
getLeasedAgentToolContext: vi.fn(),
|
|
@@ -375,6 +378,7 @@ describe('appendLogs', () => {
|
|
|
375
378
|
const workflows = createFakeInterModuleClients({
|
|
376
379
|
workflows: defineInterModulePresentation(workflowsInterModuleContract, {
|
|
377
380
|
startRunFromTrigger: vi.fn(),
|
|
381
|
+
resolveWorkflowRunTriggerReference: vi.fn(),
|
|
378
382
|
deliverEventToJobListener: vi.fn(),
|
|
379
383
|
getStepLogContext: () => ({harness: 'claude' as const}),
|
|
380
384
|
getLeasedAgentToolContext: vi.fn(),
|
package/src/core/append-logs.ts
CHANGED
|
@@ -124,7 +124,7 @@ function parseAppendBody(body: Buffer): ParsedBody {
|
|
|
124
124
|
|
|
125
125
|
/**
|
|
126
126
|
* A line that fails the raw write union but is a valid record under the read union
|
|
127
|
-
* can only be a server-only `capped`/`runner_lost` tombstone
|
|
127
|
+
* can only be a server-only `capped`/`runner_lost` tombstone: i.e. a forgery
|
|
128
128
|
* attempt. Returns its type for the audit warn, or undefined for plain garbage.
|
|
129
129
|
*/
|
|
130
130
|
function detectForgedType(line: string): string | undefined {
|
package/src/core/budget.ts
CHANGED
|
@@ -7,7 +7,7 @@ export interface AllowedBudgetParams {
|
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Accrual budget in stored bytes: `base + rate * elapsedMinutes`, floored to an
|
|
10
|
-
* integer. No hard ceiling
|
|
10
|
+
* integer. No hard ceiling: job duration limits bound total volume.
|
|
11
11
|
*/
|
|
12
12
|
export function allowedBudget({
|
|
13
13
|
baseBytes,
|
package/src/core/errors.ts
CHANGED
|
@@ -8,10 +8,10 @@ export class OffsetGapError extends Error {
|
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* The append body is not whole, newline-terminated records of the raw log
|
|
11
|
-
* record contract. `forgedType` is set only for the detectable forgery case
|
|
11
|
+
* record contract. `forgedType` is set only for the detectable forgery case: a
|
|
12
12
|
* line that is a valid server-only record (`capped`/`runner_lost`) under the read
|
|
13
|
-
* union but is not valid on the raw write path
|
|
14
|
-
* without logging the payload.
|
|
13
|
+
* union but is not valid on the raw write path. The append path can emit a
|
|
14
|
+
* narrowed audit warning without logging the payload.
|
|
15
15
|
*/
|
|
16
16
|
export class MalformedLogChunkError extends Error {
|
|
17
17
|
constructor(
|
|
@@ -27,7 +27,7 @@ export class MalformedLogChunkError extends Error {
|
|
|
27
27
|
* The lease's `(workspaceId, projectId, workflowRunAttemptId)` does not match the values
|
|
28
28
|
* stamped on the existing stream row. Since these are functionally determined
|
|
29
29
|
* by `jobId` via workflows FKs, a mismatch implies a forged token or a
|
|
30
|
-
* cross-job lease confusion
|
|
30
|
+
* cross-job lease confusion. It is never a legitimate request.
|
|
31
31
|
*/
|
|
32
32
|
export class LeaseStreamMismatchError extends Error {
|
|
33
33
|
constructor() {
|
|
@@ -96,6 +96,7 @@ describe('finalizeAttemptLogStream', () => {
|
|
|
96
96
|
const workflows = createFakeInterModuleClients({
|
|
97
97
|
workflows: defineInterModulePresentation(workflowsInterModuleContract, {
|
|
98
98
|
startRunFromTrigger: vi.fn(),
|
|
99
|
+
resolveWorkflowRunTriggerReference: vi.fn(),
|
|
99
100
|
deliverEventToJobListener: vi.fn(),
|
|
100
101
|
getStepLogContext: () => ({harness: 'claude' as const}),
|
|
101
102
|
getLeasedAgentToolContext: vi.fn(),
|
|
@@ -17,7 +17,7 @@ import {pgTable} from './common.js';
|
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* One stream per (job, step, attempt). Identity is scoped by `job_id` (from the
|
|
20
|
-
* lease), so a lease can only ever reach its own job's streams
|
|
20
|
+
* lease), so a lease can only ever reach its own job's streams; cross-job log
|
|
21
21
|
* injection is structurally impossible. `workspace_id`, `project_id`, and `workflow_run_attempt_id`
|
|
22
22
|
* are stamped from the lease at create time; they are functionally determined by
|
|
23
23
|
* `job_id` via workflows FKs, so they are denormalized here for self-contained
|
|
@@ -32,7 +32,7 @@ import {pgTable} from './common.js';
|
|
|
32
32
|
* Per-row `committed_length` and `declared_total_bytes` are bounded by the
|
|
33
33
|
* per-job budget, so `mode: 'number'` is safe on the hot path. Any cross-row
|
|
34
34
|
* aggregate (workspace or system-wide totals) MUST read as bigint at the
|
|
35
|
-
* query site
|
|
35
|
+
* query site: the global sum is unbounded and would silently lose precision
|
|
36
36
|
* past 2^53 as a JS number.
|
|
37
37
|
*/
|
|
38
38
|
export const attemptStreams = pgTable(
|
|
@@ -11,7 +11,7 @@ import {pgTable} from './common.js';
|
|
|
11
11
|
*
|
|
12
12
|
* Per-row `stored_bytes_used` is bounded by the per-job budget, so `mode:
|
|
13
13
|
* 'number'` is safe on the hot path. Any cross-row aggregate (workspace or
|
|
14
|
-
* system-wide totals) MUST read as bigint at the query site
|
|
14
|
+
* system-wide totals) MUST read as bigint at the query site: the global sum
|
|
15
15
|
* is unbounded and would silently lose precision past 2^53 as a JS number.
|
|
16
16
|
*/
|
|
17
17
|
export const jobAccounting = pgTable('job_accounting', {
|
|
@@ -9,6 +9,7 @@ export function createTestWorkflowsClient(): WorkflowsModuleClient {
|
|
|
9
9
|
return createFakeInterModuleClients({
|
|
10
10
|
workflows: defineInterModulePresentation(workflowsInterModuleContract, {
|
|
11
11
|
startRunFromTrigger: vi.fn(),
|
|
12
|
+
resolveWorkflowRunTriggerReference: vi.fn(),
|
|
12
13
|
deliverEventToJobListener: vi.fn(),
|
|
13
14
|
getStepLogContext: () => ({harness: 'pi' as const}),
|
|
14
15
|
getLeasedAgentToolContext: vi.fn(),
|
package/test/globalSetup.ts
CHANGED
|
@@ -11,7 +11,7 @@ const BUCKET_READY_POLL_INTERVAL_MS = 500;
|
|
|
11
11
|
|
|
12
12
|
// `garage-init` exits 0 once it has posted the key/bucket ACLs, but Garage does not guarantee
|
|
13
13
|
// those are immediately usable for S3 uploads. Poll HeadBucket so the suite never starts before
|
|
14
|
-
// the store will accept the configured credentials
|
|
14
|
+
// the store will accept the configured credentials: without this, the compaction tests race the
|
|
15
15
|
// ACL propagation and fail with `AccessDenied: No such key`.
|
|
16
16
|
async function waitForBucketReachable(timeoutMs: number): Promise<void> {
|
|
17
17
|
const deadline = Date.now() + timeoutMs;
|