@steve31415/baselib 3.39.0 → 3.40.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/dist/bin/pw-deploy.js +6 -4
- package/dist/bin/pw-logs.js +2 -1
- package/dist/bin/pw-rollback.js +2 -1
- package/dist/bin/pw-sql.js +2 -1
- package/dist/bin/release.js +2 -1
- package/dist/llm/complete.js +8 -0
- package/dist/llm/providers/anthropic.js +1 -0
- package/dist/llm/providers/gemini.js +1 -0
- package/dist/llm/providers/openai.js +1 -0
- package/dist/llm/types.d.ts +3 -0
- package/dist/llm/types.js +3 -0
- package/dist/ops/cli.d.ts +12 -0
- package/dist/ops/cli.js +26 -0
- package/package.json +1 -1
package/dist/bin/pw-deploy.js
CHANGED
|
@@ -3,12 +3,14 @@
|
|
|
3
3
|
// ~/migration/research/deploy-procedure-design.md). All logic lives in
|
|
4
4
|
// ../deploy/deploy.ts; configuration is the app's deploy.config.json.
|
|
5
5
|
//
|
|
6
|
-
//
|
|
7
|
-
// a failed deploy's process alive; the server-side build finishes on its
|
|
8
|
-
// and is reused by the next attempt.
|
|
6
|
+
// An explicit exit (not exitCode) so an in-flight Cloud Build child cannot
|
|
7
|
+
// keep a failed deploy's process alive; the server-side build finishes on its
|
|
8
|
+
// own and is reused by the next attempt. exitWhenFlushed lets the output
|
|
9
|
+
// already written reach a piped reader (Coder captures it) first.
|
|
10
|
+
import { exitWhenFlushed } from '../ops/cli.js';
|
|
9
11
|
import { realRunner } from '../deploy/exec.js';
|
|
10
12
|
import { runDeploy } from '../deploy/deploy.js';
|
|
11
|
-
|
|
13
|
+
await exitWhenFlushed(await runDeploy({
|
|
12
14
|
repoRoot: process.cwd(),
|
|
13
15
|
runner: realRunner,
|
|
14
16
|
argv: process.argv.slice(2),
|
package/dist/bin/pw-logs.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// `pw-logs` — Axiom log query (OPERATIONS.md "Investigating production
|
|
3
3
|
// state"). All logic lives in ../ops/logs.ts.
|
|
4
|
+
import { exitWhenFlushed } from '../ops/cli.js';
|
|
4
5
|
import { runLogs } from '../ops/logs.js';
|
|
5
|
-
|
|
6
|
+
await exitWhenFlushed(await runLogs({ argv: process.argv.slice(2), cwd: process.cwd() }));
|
package/dist/bin/pw-rollback.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// `pw-rollback` — one-command traffic move to a previously released build.
|
|
3
3
|
// All logic lives in ../deploy/rollback.ts.
|
|
4
|
+
import { exitWhenFlushed } from '../ops/cli.js';
|
|
4
5
|
import { realRunner } from '../deploy/exec.js';
|
|
5
6
|
import { runRollback } from '../deploy/rollback.js';
|
|
6
|
-
|
|
7
|
+
await exitWhenFlushed(await runRollback({
|
|
7
8
|
repoRoot: process.cwd(),
|
|
8
9
|
runner: realRunner,
|
|
9
10
|
argv: process.argv.slice(2),
|
package/dist/bin/pw-sql.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// `pw-sql` — read-only production SQL (OPERATIONS.md "Investigating
|
|
3
3
|
// production state"). All logic lives in ../ops/sql.ts.
|
|
4
|
+
import { exitWhenFlushed } from '../ops/cli.js';
|
|
4
5
|
import { runSql } from '../ops/sql.js';
|
|
5
|
-
|
|
6
|
+
await exitWhenFlushed(await runSql({ argv: process.argv.slice(2), cwd: process.cwd() }));
|
package/dist/bin/release.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// baselib's `npm run deploy` — release the checked-out version (tag push +
|
|
3
3
|
// wait for the registry; the tag-triggered workflow does the actual
|
|
4
4
|
// publish). All logic lives in ../release.ts.
|
|
5
|
+
import { exitWhenFlushed } from '../ops/cli.js';
|
|
5
6
|
import { realRunner } from '../deploy/exec.js';
|
|
6
7
|
import { release } from '../release.js';
|
|
7
8
|
try {
|
|
@@ -12,5 +13,5 @@ try {
|
|
|
12
13
|
}
|
|
13
14
|
catch (e) {
|
|
14
15
|
console.error(e instanceof Error ? e.message : String(e));
|
|
15
|
-
|
|
16
|
+
await exitWhenFlushed(1);
|
|
16
17
|
}
|
package/dist/llm/complete.js
CHANGED
|
@@ -83,6 +83,7 @@ export async function llmComplete(request) {
|
|
|
83
83
|
continue;
|
|
84
84
|
}
|
|
85
85
|
const provider = providers[modelEntry.provider];
|
|
86
|
+
const attemptStartedAt = Date.now();
|
|
86
87
|
try {
|
|
87
88
|
const result = await provider.complete({
|
|
88
89
|
apiModelId: modelEntry.apiModelId,
|
|
@@ -124,6 +125,7 @@ export async function llmComplete(request) {
|
|
|
124
125
|
fallbackModel: modelName,
|
|
125
126
|
fallbackProvider: modelEntry.provider,
|
|
126
127
|
attemptsBeforeSuccess: failedAttempts.length,
|
|
128
|
+
...(request.callSite ? { callSite: request.callSite } : {}),
|
|
127
129
|
});
|
|
128
130
|
}
|
|
129
131
|
return {
|
|
@@ -162,6 +164,12 @@ export async function llmComplete(request) {
|
|
|
162
164
|
declined: err instanceof LLMError && err.declined === true,
|
|
163
165
|
attemptNumber: failedAttempts.length,
|
|
164
166
|
nextModel: modelsToTry[failedAttempts.length] ?? 'none',
|
|
167
|
+
// Which caller, and whether it ran out its own deadline: a timeout tail is a
|
|
168
|
+
// headroom question for that call site, an HTTP error is not (reader task 58b65ec9).
|
|
169
|
+
...(request.callSite ? { callSite: request.callSite } : {}),
|
|
170
|
+
timedOut: err instanceof LLMError && err.timedOut === true,
|
|
171
|
+
timeoutMs,
|
|
172
|
+
attemptMs: Date.now() - attemptStartedAt,
|
|
165
173
|
});
|
|
166
174
|
}
|
|
167
175
|
}
|
package/dist/llm/types.d.ts
CHANGED
|
@@ -155,6 +155,8 @@ export declare class LLMError extends Error {
|
|
|
155
155
|
* not a defect.
|
|
156
156
|
*/
|
|
157
157
|
declined: boolean;
|
|
158
|
+
/** True when the attempt hit its per-provider `timeoutMs` deadline. */
|
|
159
|
+
timedOut: boolean;
|
|
158
160
|
constructor(opts: {
|
|
159
161
|
message: string;
|
|
160
162
|
provider: ProviderName;
|
|
@@ -162,6 +164,7 @@ export declare class LLMError extends Error {
|
|
|
162
164
|
statusCode?: number;
|
|
163
165
|
isRetryable: boolean;
|
|
164
166
|
declined?: boolean;
|
|
167
|
+
timedOut?: boolean;
|
|
165
168
|
});
|
|
166
169
|
}
|
|
167
170
|
export interface ProviderAdapter {
|
package/dist/llm/types.js
CHANGED
|
@@ -20,6 +20,8 @@ export class LLMError extends Error {
|
|
|
20
20
|
* not a defect.
|
|
21
21
|
*/
|
|
22
22
|
declined;
|
|
23
|
+
/** True when the attempt hit its per-provider `timeoutMs` deadline. */
|
|
24
|
+
timedOut;
|
|
23
25
|
constructor(opts) {
|
|
24
26
|
super(opts.message);
|
|
25
27
|
this.name = 'LLMError';
|
|
@@ -28,5 +30,6 @@ export class LLMError extends Error {
|
|
|
28
30
|
this.statusCode = opts.statusCode;
|
|
29
31
|
this.isRetryable = opts.isRetryable;
|
|
30
32
|
this.declined = opts.declined ?? false;
|
|
33
|
+
this.timedOut = opts.timedOut ?? false;
|
|
31
34
|
}
|
|
32
35
|
}
|
package/dist/ops/cli.d.ts
CHANGED
|
@@ -13,4 +13,16 @@ type Parsed<T extends Options> = ReturnType<typeof parseArgs<{
|
|
|
13
13
|
export declare function parseCli<T extends Options>(argv: string[], options: T, usage: string, io: CliIo): Parsed<T> | {
|
|
14
14
|
code: number;
|
|
15
15
|
};
|
|
16
|
+
/**
|
|
17
|
+
* `process.exit(code)` once stdout and stderr have handed everything written
|
|
18
|
+
* to them to the OS. A plain process.exit discards what a pipe has not yet
|
|
19
|
+
* taken: Node writes to a pipe asynchronously once its buffer (64 KiB on
|
|
20
|
+
* Linux) is full, so `pw-logs … | jq` lost all but the first 64 KiB, with
|
|
21
|
+
* exit status 0 (2026-10-26). Files and terminals are written synchronously,
|
|
22
|
+
* which is why redirecting to a file never showed it. Still an explicit exit,
|
|
23
|
+
* not `exitCode`: nothing else the bin started (a connection pool, a Cloud
|
|
24
|
+
* Build child) may keep it alive. A reader that goes away (`| head`) ends the
|
|
25
|
+
* wait instead of crashing the bin on EPIPE.
|
|
26
|
+
*/
|
|
27
|
+
export declare function exitWhenFlushed(code: number, streams?: NodeJS.WritableStream[], exit?: (code: number) => void): Promise<void>;
|
|
16
28
|
export {};
|
package/dist/ops/cli.js
CHANGED
|
@@ -25,3 +25,29 @@ export function parseCli(argv, options, usage, io) {
|
|
|
25
25
|
}
|
|
26
26
|
return parsed;
|
|
27
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* `process.exit(code)` once stdout and stderr have handed everything written
|
|
30
|
+
* to them to the OS. A plain process.exit discards what a pipe has not yet
|
|
31
|
+
* taken: Node writes to a pipe asynchronously once its buffer (64 KiB on
|
|
32
|
+
* Linux) is full, so `pw-logs … | jq` lost all but the first 64 KiB, with
|
|
33
|
+
* exit status 0 (2026-10-26). Files and terminals are written synchronously,
|
|
34
|
+
* which is why redirecting to a file never showed it. Still an explicit exit,
|
|
35
|
+
* not `exitCode`: nothing else the bin started (a connection pool, a Cloud
|
|
36
|
+
* Build child) may keep it alive. A reader that goes away (`| head`) ends the
|
|
37
|
+
* wait instead of crashing the bin on EPIPE.
|
|
38
|
+
*/
|
|
39
|
+
export async function exitWhenFlushed(code, streams = [process.stdout, process.stderr], exit = (c) => process.exit(c)) {
|
|
40
|
+
await Promise.all(streams.map(flushed));
|
|
41
|
+
exit(code);
|
|
42
|
+
}
|
|
43
|
+
function flushed(stream) {
|
|
44
|
+
return new Promise((resolve) => {
|
|
45
|
+
// A broken pipe is reported to the write callback and then, a tick
|
|
46
|
+
// later, as an 'error' event, which would crash the process if nothing
|
|
47
|
+
// listened — so the listener stays until the exit.
|
|
48
|
+
stream.on('error', () => resolve());
|
|
49
|
+
// Writes complete in order, so an empty write's callback runs only after
|
|
50
|
+
// everything written before it has gone out.
|
|
51
|
+
stream.write('', () => resolve());
|
|
52
|
+
});
|
|
53
|
+
}
|
package/package.json
CHANGED