@crouton-kit/humanloop 0.3.33 → 0.3.34
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/api.d.ts +4 -8
- package/dist/api.js +6 -29
- package/dist/browser/server.d.ts +3 -3
- package/dist/browser/server.js +22 -25
- package/dist/cli.js +126 -1114
- package/dist/editor/feedback.d.ts +9 -0
- package/dist/editor/feedback.js +23 -0
- package/dist/editor/review.d.ts +8 -30
- package/dist/editor/review.js +79 -118
- package/dist/inbox/claim.d.ts +23 -0
- package/dist/inbox/claim.js +67 -0
- package/dist/inbox/completion.d.ts +4 -0
- package/dist/inbox/completion.js +100 -0
- package/dist/inbox/controller.d.ts +57 -0
- package/dist/inbox/controller.js +311 -0
- package/dist/inbox/convention.d.ts +15 -10
- package/dist/inbox/convention.js +149 -79
- package/dist/inbox/deck-adapter.d.ts +27 -0
- package/dist/inbox/deck-adapter.js +57 -0
- package/dist/inbox/deck-schema.d.ts +18 -14
- package/dist/inbox/deck-schema.js +59 -117
- package/dist/inbox/layout.d.ts +8 -0
- package/dist/inbox/layout.js +9 -0
- package/dist/inbox/registry.d.ts +29 -0
- package/dist/inbox/registry.js +97 -0
- package/dist/inbox/review-adapter.d.ts +22 -0
- package/dist/inbox/review-adapter.js +56 -0
- package/dist/inbox/scan.d.ts +3 -2
- package/dist/inbox/scan.js +71 -43
- package/dist/inbox/tickets.d.ts +63 -0
- package/dist/inbox/tickets.js +247 -0
- package/dist/inbox/tui.d.ts +3 -6
- package/dist/inbox/tui.js +24 -112
- package/dist/index.d.ts +12 -3
- package/dist/index.js +8 -2
- package/dist/summary.d.ts +2 -3
- package/dist/summary.js +2 -3
- package/dist/surfaces/inbox-popup.d.ts +2 -0
- package/dist/surfaces/inbox-popup.js +32 -0
- package/dist/tui/app.js +13 -0
- package/dist/tui/tmux.d.ts +30 -7
- package/dist/tui/tmux.js +179 -66
- package/dist/types.d.ts +68 -7
- package/package.json +2 -2
package/dist/api.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Deck, ResolutionEnvelope
|
|
1
|
+
import type { Deck, ResolutionEnvelope } from './types.js';
|
|
2
2
|
export interface AskOpts {
|
|
3
3
|
/** Interaction directory. Defaults to a managed temp dir under os.tmpdir(). */
|
|
4
4
|
dir?: string;
|
|
@@ -17,11 +17,7 @@ export declare function notify(title: string, body?: string): Promise<void>;
|
|
|
17
17
|
export interface InboxOpts {
|
|
18
18
|
cols?: number;
|
|
19
19
|
rows?: number;
|
|
20
|
-
|
|
20
|
+
roots?: string[];
|
|
21
21
|
}
|
|
22
|
-
/**
|
|
23
|
-
|
|
24
|
-
* human pick one, resolves it (writing its `response.json`), then rescans —
|
|
25
|
-
* resolved items drop out — until the human quits or nothing is pending.
|
|
26
|
-
*/
|
|
27
|
-
export declare function inbox(roots: string[], opts?: InboxOpts): Promise<void>;
|
|
22
|
+
/** Open the centralized inbox controller in the current human terminal. */
|
|
23
|
+
export declare function openInbox(opts?: InboxOpts): Promise<void>;
|
package/dist/api.js
CHANGED
|
@@ -2,11 +2,9 @@ import { mkdtempSync, mkdirSync } from 'node:fs';
|
|
|
2
2
|
import { tmpdir } from 'node:os';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import { resolveInteractionDir } from './tui/app.js';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { deckPath, atomicWriteJson, readJson, stampCanvasNode } from './inbox/convention.js';
|
|
5
|
+
import { InboxController } from './inbox/controller.js';
|
|
6
|
+
import { deckPath, atomicWriteJson, stampCanvasNode } from './inbox/convention.js';
|
|
8
7
|
import { resolveDeckBodyPaths } from './inbox/deck-schema.js';
|
|
9
|
-
import { getTerminalSize } from './tui/terminal.js';
|
|
10
8
|
import { notifyDeck } from './inbox/deck-factories.js';
|
|
11
9
|
import { buildSummary } from './summary.js';
|
|
12
10
|
const RESPONSE_SCHEMA_ID = 'humanloop.response/v2';
|
|
@@ -48,29 +46,8 @@ export async function notify(title, body) {
|
|
|
48
46
|
const deck = notifyDeck(title, body !== undefined ? { body } : {});
|
|
49
47
|
await ask(deck, {});
|
|
50
48
|
}
|
|
51
|
-
/**
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
*/
|
|
56
|
-
export async function inbox(roots, opts = {}) {
|
|
57
|
-
for (;;) {
|
|
58
|
-
const items = scanInbox(roots);
|
|
59
|
-
if (items.length === 0)
|
|
60
|
-
return;
|
|
61
|
-
const term = getTerminalSize();
|
|
62
|
-
const cols = opts.cols ?? term.cols;
|
|
63
|
-
const rows = opts.rows ?? term.rows;
|
|
64
|
-
const picked = await pickFromInbox(items, { cols, rows });
|
|
65
|
-
if (picked === null)
|
|
66
|
-
return;
|
|
67
|
-
const deck = readJson(deckPath(picked.dir));
|
|
68
|
-
if (deck === null)
|
|
69
|
-
continue; // raced/removed — rescan
|
|
70
|
-
await resolveInteractionDir(picked.dir, deck, {
|
|
71
|
-
generateVisual: opts.generateVisual,
|
|
72
|
-
cols,
|
|
73
|
-
rows,
|
|
74
|
-
});
|
|
75
|
-
}
|
|
49
|
+
/** Open the centralized inbox controller in the current human terminal. */
|
|
50
|
+
export async function openInbox(opts = {}) {
|
|
51
|
+
const controller = new InboxController({ roots: opts.roots, cols: opts.cols, rows: opts.rows });
|
|
52
|
+
await controller.run();
|
|
76
53
|
}
|
package/dist/browser/server.d.ts
CHANGED
|
@@ -17,15 +17,15 @@ export interface WebServerOpts {
|
|
|
17
17
|
onSubmit?: (responses: InteractionResponse[], completedAt: string, responsePath: string) => void;
|
|
18
18
|
}
|
|
19
19
|
export interface ReviewWebServerOpts {
|
|
20
|
-
/** Shared review job dir — carries review.vim and
|
|
20
|
+
/** Shared review job dir — carries review.vim and progress.json. */
|
|
21
21
|
jobDir: string;
|
|
22
22
|
/** Absolute source file under review. */
|
|
23
23
|
file: string;
|
|
24
|
-
/** Absolute
|
|
24
|
+
/** Absolute resumable review draft path. */
|
|
25
25
|
output: string;
|
|
26
26
|
/** Optional submit sentinel written on final browser submit. */
|
|
27
27
|
submitFlagPath?: string;
|
|
28
|
-
/** Fired after the HTTP
|
|
28
|
+
/** Fired after the HTTP proposal ack has finished flushing. The controller owns finalization. */
|
|
29
29
|
onSubmit?: (result: FeedbackResult, submittedAt: string, outputPath: string) => void;
|
|
30
30
|
/** Test-only override for how long `requestTakeBack()` waits for open tabs
|
|
31
31
|
* to ack a flush before giving up and broadcasting `taken-back` anyway.
|
package/dist/browser/server.js
CHANGED
|
@@ -5,7 +5,7 @@ import { join, extname, dirname, basename } from 'node:path';
|
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
6
|
import { WebSocketServer } from 'ws';
|
|
7
7
|
import { deckPath, readJson, writeResponse, clearProgress } from '../inbox/convention.js';
|
|
8
|
-
import { buildDraftFeedbackResult, buildFinalFeedbackResult, parseFeedbackComments,
|
|
8
|
+
import { buildDraftFeedbackResult, buildFinalFeedbackResult, parseFeedbackComments, readReviewDraft, serializeFeedbackResult, writeReviewDraft, writeSubmitFlag, } from '../editor/feedback.js';
|
|
9
9
|
// See $CRTR_CONTEXT_DIR/phase1-server-contract.md for the deck HTTP/WS API this
|
|
10
10
|
// module implements — this file is the reference implementation for the deck
|
|
11
11
|
// surface, and the review surface reuses the same static/WS skeleton.
|
|
@@ -201,20 +201,22 @@ async function startReviewServer(opts) {
|
|
|
201
201
|
const { jobDir, file, output, submitFlagPath } = opts;
|
|
202
202
|
const scaffold = createServerScaffold();
|
|
203
203
|
let activated = false;
|
|
204
|
-
|
|
204
|
+
const persistedDraft = readReviewDraft(output);
|
|
205
|
+
let version = persistedDraft?.version ?? 0;
|
|
205
206
|
let ackWaiter = null;
|
|
206
207
|
const TAKE_BACK_ACK_TIMEOUT_MS = opts.takeBackAckTimeoutMs ?? 2000;
|
|
207
|
-
let currentResult =
|
|
208
|
-
|
|
208
|
+
let currentResult = buildDraftFeedbackResult(file, persistedDraft?.comments ?? [], persistedDraft?.savedAt ?? new Date().toISOString());
|
|
209
|
+
let proposed = false;
|
|
209
210
|
function refreshInitialDraftFromDisk() {
|
|
210
|
-
if (
|
|
211
|
+
if (proposed)
|
|
211
212
|
return;
|
|
212
|
-
const onDisk =
|
|
213
|
-
if (onDisk === null)
|
|
213
|
+
const onDisk = readReviewDraft(output);
|
|
214
|
+
if (onDisk === null || onDisk.version < version)
|
|
214
215
|
return;
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
216
|
+
const next = buildDraftFeedbackResult(file, onDisk.comments, onDisk.savedAt);
|
|
217
|
+
if (onDisk.version > version || serializeFeedbackResult(next) !== serializeFeedbackResult(currentResult)) {
|
|
218
|
+
currentResult = next;
|
|
219
|
+
version = onDisk.version;
|
|
218
220
|
}
|
|
219
221
|
}
|
|
220
222
|
function currentSnapshot() {
|
|
@@ -297,7 +299,7 @@ async function startReviewServer(opts) {
|
|
|
297
299
|
return;
|
|
298
300
|
}
|
|
299
301
|
refreshInitialDraftFromDisk();
|
|
300
|
-
if (
|
|
302
|
+
if (proposed) {
|
|
301
303
|
sendJson(res, 409, { error: 'already_submitted', result: currentSnapshot() });
|
|
302
304
|
return;
|
|
303
305
|
}
|
|
@@ -317,7 +319,7 @@ async function startReviewServer(opts) {
|
|
|
317
319
|
const savedAt = new Date().toISOString();
|
|
318
320
|
const nextResult = buildDraftFeedbackResult(file, parsedComments.comments, savedAt);
|
|
319
321
|
try {
|
|
320
|
-
|
|
322
|
+
writeReviewDraft(output, parsedComments.comments, version + 1, savedAt);
|
|
321
323
|
}
|
|
322
324
|
catch (err) {
|
|
323
325
|
sendJson(res, 500, { error: 'internal', message: err instanceof Error ? err.message : String(err) });
|
|
@@ -343,14 +345,8 @@ async function startReviewServer(opts) {
|
|
|
343
345
|
return;
|
|
344
346
|
}
|
|
345
347
|
refreshInitialDraftFromDisk();
|
|
346
|
-
if (
|
|
347
|
-
sendJson(res, 409, {
|
|
348
|
-
ok: false,
|
|
349
|
-
error: 'already_submitted',
|
|
350
|
-
output,
|
|
351
|
-
submittedAt: currentResult.submittedAt,
|
|
352
|
-
result: currentSnapshot(),
|
|
353
|
-
});
|
|
348
|
+
if (proposed) {
|
|
349
|
+
sendJson(res, 409, { ok: false, error: 'already_submitted', output, result: currentSnapshot() });
|
|
354
350
|
return;
|
|
355
351
|
}
|
|
356
352
|
if (!Number.isInteger(parsed.baseVersion)) {
|
|
@@ -369,11 +365,12 @@ async function startReviewServer(opts) {
|
|
|
369
365
|
const submittedAt = new Date().toISOString();
|
|
370
366
|
const nextResult = buildFinalFeedbackResult(file, parsedComments.comments, submittedAt);
|
|
371
367
|
try {
|
|
372
|
-
|
|
373
|
-
currentResult =
|
|
374
|
-
|
|
368
|
+
writeReviewDraft(output, parsedComments.comments, version + 1, submittedAt);
|
|
369
|
+
currentResult = buildDraftFeedbackResult(file, parsedComments.comments, submittedAt);
|
|
370
|
+
version += 1;
|
|
371
|
+
proposed = true;
|
|
372
|
+
if (submitFlagPath !== undefined && submitFlagPath.length > 0)
|
|
375
373
|
writeSubmitFlag(submitFlagPath);
|
|
376
|
-
}
|
|
377
374
|
}
|
|
378
375
|
catch (err) {
|
|
379
376
|
sendJson(res, 500, { error: 'internal', message: err instanceof Error ? err.message : String(err) });
|
|
@@ -384,7 +381,7 @@ async function startReviewServer(opts) {
|
|
|
384
381
|
// between a lost WS frame and the teardown it precedes.
|
|
385
382
|
await scaffold.broadcast({ type: 'converged' });
|
|
386
383
|
res.once('finish', () => {
|
|
387
|
-
opts.onSubmit?.(
|
|
384
|
+
opts.onSubmit?.(nextResult, submittedAt, output);
|
|
388
385
|
});
|
|
389
386
|
sendJson(res, 200, { ok: true, output, submittedAt, result: currentSnapshot() });
|
|
390
387
|
return;
|