@astrosheep/square 0.3.61 → 0.3.62
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/claude-plugin/.claude-plugin/plugin.json +1 -1
- package/claude-plugin/skills/square/SKILL.md +5 -7
- package/codex-plugin/.codex-plugin/plugin.json +1 -1
- package/dist/activity.d.ts +6 -2
- package/dist/activity.js +168 -133
- package/dist/artifact.d.ts +4 -0
- package/dist/artifact.js +43 -14
- package/dist/attention-presentation.d.ts +1 -0
- package/dist/attention-presentation.js +8 -6
- package/dist/automatic-session.js +14 -24
- package/dist/boundary-presentation.d.ts +1 -2
- package/dist/boundary-presentation.js +4 -19
- package/dist/cli/context.d.ts +2 -0
- package/dist/cli/context.js +2 -18
- package/dist/cli/harness-command.js +1 -1
- package/dist/cli/mcp-server-command.d.ts +2 -0
- package/dist/cli/mcp-server-command.js +13 -0
- package/dist/cli/observation-commands.js +47 -19
- package/dist/cli/program.js +5 -2
- package/dist/cli/registry.js +2 -0
- package/dist/cli/square-commands.d.ts +1 -1
- package/dist/cli/square-commands.js +93 -125
- package/dist/decisions.d.ts +2 -0
- package/dist/decisions.js +44 -3
- package/dist/delivery-operations.d.ts +1 -2
- package/dist/delivery-operations.js +1 -4
- package/dist/file-changes.d.ts +12 -0
- package/dist/file-changes.js +235 -0
- package/dist/help.js +4 -8
- package/dist/host-ledger-file-adapter.d.ts +15 -13
- package/dist/host-ledger-file-adapter.js +131 -79
- package/dist/host-ledger-root.d.ts +2 -0
- package/dist/host-ledger-root.js +9 -0
- package/dist/host-ledger.d.ts +4 -24
- package/dist/inbox.d.ts +10 -5
- package/dist/inbox.js +152 -67
- package/dist/index.d.ts +3 -1
- package/dist/index.js +1 -0
- package/dist/mcp.d.ts +7 -0
- package/dist/mcp.js +122 -0
- package/dist/mention-parse.d.ts +8 -0
- package/dist/mention-parse.js +107 -0
- package/dist/model.d.ts +5 -0
- package/dist/model.js +4 -1
- package/dist/notifications.d.ts +1 -0
- package/dist/notifications.js +25 -20
- package/dist/participant-host.js +2 -2
- package/dist/ports.d.ts +2 -9
- package/dist/presentation.d.ts +12 -1
- package/dist/presentation.js +42 -12
- package/dist/presented.js +2 -1
- package/dist/registry.d.ts +8 -4
- package/dist/registry.js +41 -74
- package/dist/routes.d.ts +19 -2
- package/dist/routes.js +48 -5
- package/dist/runtime.d.ts +2 -0
- package/dist/runtime.js +2 -0
- package/dist/square-actions.d.ts +26 -13
- package/dist/square-actions.js +200 -84
- package/dist/square-application.d.ts +58 -0
- package/dist/square-application.js +183 -0
- package/dist/square-facade.d.ts +24 -9
- package/dist/square-file-adapter.d.ts +5 -12
- package/dist/square-file-adapter.js +12 -64
- package/dist/square-projections.d.ts +3 -2
- package/dist/square-projections.js +12 -3
- package/dist/square-storage.js +27 -34
- package/dist/square-wiring.d.ts +12 -7
- package/dist/square-wiring.js +36 -32
- package/dist/state-cell.d.ts +1 -1
- package/dist/stream.js +30 -26
- package/dist/views.d.ts +3 -3
- package/dist/views.js +4 -4
- package/dist/wake-attempts.js +2 -1
- package/dist/wake-evidence.js +2 -8
- package/dist/wakes.d.ts +1 -0
- package/dist/wakes.js +5 -5
- package/dist/watch.d.ts +6 -1
- package/dist/watch.js +20 -18
- package/extensions/square-pi.js +100 -112
- package/guides/application-boundary.md +32 -0
- package/guides/change-waits.md +28 -0
- package/guides/host-ledger.md +9 -0
- package/guides/mcp.md +9 -0
- package/guides/operation-cancellation.md +17 -0
- package/package.json +8 -2
- package/skills/square/SKILL.md +5 -7
package/dist/square-wiring.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { closeOpenSquare } from './open-square.js';
|
|
2
2
|
import { buildMemorySquare, buildSquare, openSquare } from './square-file-adapter.js';
|
|
3
|
-
import { catchUp, done, endOwnedSession, express, hold, ignore, implicitJoin, join, takeover, listen, listening, resume, } from './square-actions.js';
|
|
3
|
+
import { catchUp, done, endOwnedSession, express, hold, ignore, implicitJoin, join, takeover, listen, listening, resume, retireEndedSessionRoutes, } from './square-actions.js';
|
|
4
4
|
import { markBoundarySeen as recordBoundarySeen } from './presence.js';
|
|
5
5
|
import { history, participantHistory, participants, resolveParticipant, snapshot } from './views.js';
|
|
6
6
|
import { currentParticipant } from './views.js';
|
|
7
7
|
import { projectSessionBindings } from './square-projections.js';
|
|
8
|
-
import { reconcileBinding as reconcileBindingOperation } from './delivery-operations.js';
|
|
9
8
|
class ParticipantHandle {
|
|
10
9
|
name;
|
|
11
10
|
square;
|
|
@@ -15,32 +14,32 @@ class ParticipantHandle {
|
|
|
15
14
|
this.square = square;
|
|
16
15
|
this.context = context;
|
|
17
16
|
}
|
|
18
|
-
express(body, options) {
|
|
19
|
-
return express(this.context, this.name, body, options);
|
|
17
|
+
express(body, options, control) {
|
|
18
|
+
return express(this.context, this.name, body, options, control);
|
|
20
19
|
}
|
|
21
|
-
listen(target) {
|
|
22
|
-
return listen(this.context, this.name, target);
|
|
20
|
+
listen(target, control) {
|
|
21
|
+
return listen(this.context, this.name, target, control);
|
|
23
22
|
}
|
|
24
|
-
ignore(target) {
|
|
25
|
-
return ignore(this.context, this.name, target);
|
|
23
|
+
ignore(target, control) {
|
|
24
|
+
return ignore(this.context, this.name, target, control);
|
|
26
25
|
}
|
|
27
|
-
listening() {
|
|
28
|
-
return listening(this.context, this.name);
|
|
26
|
+
listening(control) {
|
|
27
|
+
return listening(this.context, this.name, control);
|
|
29
28
|
}
|
|
30
|
-
catch(options) {
|
|
31
|
-
return catchUp(this.context, this.name, options);
|
|
29
|
+
catch(options, control) {
|
|
30
|
+
return catchUp(this.context, this.name, options, undefined, control);
|
|
32
31
|
}
|
|
33
|
-
history(query) {
|
|
34
|
-
return participantHistory(this.square, this.name, query);
|
|
32
|
+
history(query, control) {
|
|
33
|
+
return participantHistory(this.square, this.name, query, control);
|
|
35
34
|
}
|
|
36
|
-
hold(reason) {
|
|
37
|
-
return hold(this.context, this.name, reason);
|
|
35
|
+
hold(reason, control) {
|
|
36
|
+
return hold(this.context, this.name, reason, control);
|
|
38
37
|
}
|
|
39
|
-
resume() {
|
|
40
|
-
return resume(this.context, this.name);
|
|
38
|
+
resume(control) {
|
|
39
|
+
return resume(this.context, this.name, control);
|
|
41
40
|
}
|
|
42
|
-
done(body) {
|
|
43
|
-
return done(this.context, this.name, body);
|
|
41
|
+
done(body, control) {
|
|
42
|
+
return done(this.context, this.name, body, {}, control);
|
|
44
43
|
}
|
|
45
44
|
}
|
|
46
45
|
export class Square {
|
|
@@ -61,27 +60,33 @@ export class Square {
|
|
|
61
60
|
static inMemory(input) {
|
|
62
61
|
return new Square('memory', buildMemorySquare(input));
|
|
63
62
|
}
|
|
64
|
-
async join(name) {
|
|
65
|
-
const joined = await join(this.context, name);
|
|
63
|
+
async join(name, control) {
|
|
64
|
+
const joined = await join(this.context, name, control);
|
|
66
65
|
return new ParticipantHandle(joined.name, this.square, this.context);
|
|
67
66
|
}
|
|
68
|
-
async joinWithActivity(name) {
|
|
69
|
-
const joined = await join(this.context, name);
|
|
67
|
+
async joinWithActivity(name, control) {
|
|
68
|
+
const joined = await join(this.context, name, control);
|
|
70
69
|
return { participant: new ParticipantHandle(joined.name, this.square, this.context), activity: joined.activity };
|
|
71
70
|
}
|
|
72
|
-
async
|
|
73
|
-
const result = await takeover(this.context, name);
|
|
74
|
-
return new ParticipantHandle(result.name, this.square, this.context);
|
|
71
|
+
async takeoverWithActivity(name, control) {
|
|
72
|
+
const result = await takeover(this.context, name, [], control);
|
|
73
|
+
return { participant: new ParticipantHandle(result.name, this.square, this.context), activities: result.activities };
|
|
75
74
|
}
|
|
76
|
-
async
|
|
77
|
-
|
|
75
|
+
async takeover(name, control) {
|
|
76
|
+
return (await this.takeoverWithActivity(name, control)).participant;
|
|
77
|
+
}
|
|
78
|
+
doneOwnedSession(name, body, expectedEpoch, control) {
|
|
79
|
+
return done(this.context, name, body, { expectedEpoch }, control);
|
|
80
|
+
}
|
|
81
|
+
async implicitJoin(name, control) {
|
|
82
|
+
const joined = await implicitJoin(this.context, name, control);
|
|
78
83
|
return joined.state === 'done'
|
|
79
84
|
? { state: joined.state }
|
|
80
85
|
: { state: joined.state, participant: new ParticipantHandle(joined.name, this.square, this.context) };
|
|
81
86
|
}
|
|
82
87
|
participants() { return participants(this.square); }
|
|
83
88
|
snapshot() { return snapshot(this.square); }
|
|
84
|
-
history(query) { return history(this.square, query); }
|
|
89
|
+
history(query, control) { return history(this.square, query, control); }
|
|
85
90
|
async recognize(env) {
|
|
86
91
|
if (this.square.hostLedger === undefined)
|
|
87
92
|
return null;
|
|
@@ -93,7 +98,6 @@ export class Square {
|
|
|
93
98
|
hostLedger: this.square.hostLedger,
|
|
94
99
|
location: this.square.location,
|
|
95
100
|
sessionId,
|
|
96
|
-
scopes: ['user', 'local'],
|
|
97
101
|
})))).flat();
|
|
98
102
|
if (candidates.length !== 1)
|
|
99
103
|
return null;
|
|
@@ -101,8 +105,8 @@ export class Square {
|
|
|
101
105
|
return canonicalName === undefined ? null : new ParticipantHandle(canonicalName, this.square, this.context);
|
|
102
106
|
}
|
|
103
107
|
close() { return closeOpenSquare(this.square); }
|
|
104
|
-
reconcileBinding() { return reconcileBindingOperation({ artifact: this.square.artifact, hostLedger: this.square.hostLedger, location: this.location }); }
|
|
105
108
|
endOwnedSession(name, sessionId, expectedEpoch) { return endOwnedSession(this.context, name, sessionId, expectedEpoch); }
|
|
109
|
+
retireEndedSessionRoutes(sessionId) { return retireEndedSessionRoutes(this.context, sessionId); }
|
|
106
110
|
}
|
|
107
111
|
export function markBoundarySeen(squarePath, name, actIndexes, at) {
|
|
108
112
|
return recordBoundarySeen(squarePath, name, actIndexes, at);
|
package/dist/state-cell.d.ts
CHANGED
|
@@ -9,6 +9,6 @@ export interface StateCell {
|
|
|
9
9
|
state: SquareState;
|
|
10
10
|
version: number;
|
|
11
11
|
}>;
|
|
12
|
-
changed(sinceVersion: number, timeoutMs: number): Promise<boolean>;
|
|
12
|
+
changed(sinceVersion: number, timeoutMs: number, signal?: AbortSignal): Promise<boolean>;
|
|
13
13
|
close(): Promise<void>;
|
|
14
14
|
}
|
package/dist/stream.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { once } from 'node:events';
|
|
4
|
-
import { setTimeout as sleep } from 'node:timers/promises';
|
|
5
4
|
import { quoteShell } from './presentation.js';
|
|
6
|
-
import {
|
|
7
|
-
import { openSquare } from './square-file-adapter.js';
|
|
5
|
+
import { openSquare, observeSquareChanges } from './square-file-adapter.js';
|
|
8
6
|
import { closeOpenSquare } from './open-square.js';
|
|
9
7
|
import { streamProjection, streamTailProjection } from './views.js';
|
|
10
8
|
async function writeNdjson(value) {
|
|
@@ -20,34 +18,40 @@ export async function cmdStreamNdjson(squarePath, recipient, start = { kind: 'ta
|
|
|
20
18
|
}
|
|
21
19
|
let cursor = start.kind === 'after' ? start.after : -1;
|
|
22
20
|
let initial = start.kind === 'tail' ? start : undefined;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
const observer = await observeSquareChanges(squarePath);
|
|
22
|
+
try {
|
|
23
|
+
while (true) {
|
|
24
|
+
const generation = observer.generation;
|
|
26
25
|
try {
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
26
|
+
const baseline = await observer.read();
|
|
27
|
+
const square = await openSquare(squarePath);
|
|
28
|
+
let hasMore;
|
|
29
|
+
try {
|
|
30
|
+
const projection = initial === undefined
|
|
31
|
+
? await streamProjection(square, cursor, recipient)
|
|
32
|
+
: await streamTailProjection(square, initial.last, recipient);
|
|
33
|
+
for (const item of projection.activities) {
|
|
34
|
+
await writeNdjson({ seq: item.activity.index, square: squarePath, ...item.activity,
|
|
35
|
+
...(item.route === undefined ? {} : { route: item.route }) });
|
|
36
|
+
}
|
|
37
|
+
cursor = projection.cursor;
|
|
38
|
+
initial = undefined;
|
|
39
|
+
hasMore = projection.hasMore;
|
|
37
40
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
finally {
|
|
42
|
+
await closeOpenSquare(square);
|
|
43
|
+
}
|
|
44
|
+
if (!hasMore)
|
|
45
|
+
await observer.changed(baseline, Infinity);
|
|
42
46
|
}
|
|
43
|
-
|
|
44
|
-
|
|
47
|
+
catch {
|
|
48
|
+
// Repair/replacement is a storage hint, not a reason to scan history on a timer.
|
|
49
|
+
await observer.hinted(generation, Infinity);
|
|
45
50
|
}
|
|
46
51
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
await sleep(SLEEP_MS);
|
|
52
|
+
}
|
|
53
|
+
finally {
|
|
54
|
+
observer.close();
|
|
51
55
|
}
|
|
52
56
|
}
|
|
53
57
|
export async function cmdStream(squarePath) {
|
package/dist/views.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { coreParticipants, coreStatus } from './decisions.js';
|
|
|
3
3
|
import { type DeliveryModel, type PlannedNotification } from './delivery.js';
|
|
4
4
|
import { type ActivitiesOptions, type ActivityObservation, type InboxNotification, type PublicAct, type RoomChangeAct, type SquareState, type StoredAct } from './model.js';
|
|
5
5
|
import type { OpenSquare } from './open-square.js';
|
|
6
|
-
import type { Activity, HistoryQuery, ParticipantStatus, SquareSnapshot } from './square-facade.js';
|
|
6
|
+
import type { Activity, HistoryQuery, OperationControl, ParticipantStatus, SquareSnapshot } from './square-facade.js';
|
|
7
7
|
export interface ActivityPresentation {
|
|
8
8
|
readonly name: string;
|
|
9
9
|
readonly roster: readonly string[];
|
|
@@ -71,8 +71,8 @@ export interface PendingDeliveryProjection {
|
|
|
71
71
|
readonly recipient: string;
|
|
72
72
|
readonly notifications: readonly PlannedNotification[];
|
|
73
73
|
}
|
|
74
|
-
export declare function history(square: OpenSquare, query?: HistoryQuery): Promise<Activity[]>;
|
|
75
|
-
export declare function participantHistory(square: OpenSquare, _name: string, query?: HistoryQuery): Promise<Activity[]>;
|
|
74
|
+
export declare function history(square: OpenSquare, query?: HistoryQuery, control?: OperationControl): Promise<Activity[]>;
|
|
75
|
+
export declare function participantHistory(square: OpenSquare, _name: string, query?: HistoryQuery, control?: OperationControl): Promise<Activity[]>;
|
|
76
76
|
export declare function resolveParticipant(square: OpenSquare, name: string): Promise<{
|
|
77
77
|
readonly name: string;
|
|
78
78
|
readonly roster: readonly string[];
|
package/dist/views.js
CHANGED
|
@@ -7,7 +7,7 @@ import { countSays, currentHold, foldedState, freshWatchLease, inSquareCount, is
|
|
|
7
7
|
function expose(stored) {
|
|
8
8
|
if (stored.kind === 'read' || stored.actor === undefined)
|
|
9
9
|
throw new Error(`Cannot expose stored activity ${formatActivityId(stored.index)}`);
|
|
10
|
-
return { id: formatActivityId(stored.index), at: stored.at, kind: stored.kind, actor: stored.actor, ...('body' in stored && stored.body !== undefined ? { body: stored.body } : {}), mentions: stored.kind === 'say' ? stored.mentions ?? [] : [], ...('target' in stored ? { target: stored.target } : {}), ...(stored.kind === 'say' && stored.reply !== undefined ? { reply: formatActivityId(stored.reply) } : {}) };
|
|
10
|
+
return { id: formatActivityId(stored.index), at: stored.at, kind: stored.kind, actor: stored.actor, ...(stored.kind === 'say' && stored.reach !== undefined ? { reach: stored.reach } : {}), ...('body' in stored && stored.body !== undefined ? { body: stored.body } : {}), mentions: stored.kind === 'say' ? stored.mentions ?? [] : [], ...('target' in stored ? { target: stored.target } : {}), ...(stored.kind === 'say' && stored.reply !== undefined ? { reply: formatActivityId(stored.reply) } : {}) };
|
|
11
11
|
}
|
|
12
12
|
function parseRequiredActivityId(id) {
|
|
13
13
|
const index = parseActivityId(id);
|
|
@@ -20,7 +20,7 @@ function historyOptions(query) {
|
|
|
20
20
|
throw new SquareError('invalid_args', 'History cannot combine before and after cursors');
|
|
21
21
|
const afterIndex = query.after === undefined ? undefined : parseRequiredActivityId(query.after);
|
|
22
22
|
const beforeIndex = query.before === undefined ? undefined : parseRequiredActivityId(query.before);
|
|
23
|
-
return { ...(query.from === undefined ? {} : { participants: [...query.from] }), ...(query.grep === undefined ? {} : { grep: query.grep }), ...(query.mention === undefined ? {} : { mention: query.mention }), ...(afterIndex === undefined ? {} : { afterIndex }), ...(beforeIndex === undefined ? {} : { beforeIndex }), order: 'asc' };
|
|
23
|
+
return { ...(query.from === undefined ? {} : { participants: [...query.from] }), ...(query.grep === undefined ? {} : { grep: query.grep }), ...(query.fixed === undefined ? {} : { fixed: query.fixed }), ...(query.mention === undefined ? {} : { mention: query.mention }), ...(afterIndex === undefined ? {} : { afterIndex }), ...(beforeIndex === undefined ? {} : { beforeIndex }), order: 'asc' };
|
|
24
24
|
}
|
|
25
25
|
function selectHistory(stored, query) {
|
|
26
26
|
let selected = stored.filter((activity) => activity.kind !== 'read');
|
|
@@ -59,8 +59,8 @@ function sayNumbers(state) {
|
|
|
59
59
|
}
|
|
60
60
|
return result;
|
|
61
61
|
}
|
|
62
|
-
export async function history(square, query = {}) { const { state } = await square.artifact.read(); return selectHistory(coreActivities(state, historyOptions(query)), query).map(expose); }
|
|
63
|
-
export async function participantHistory(square, _name, query = {}) { const { state } = await square.artifact.read(); const effective = query.limit !== undefined ? query : { ...query, limit: 10 }; return selectHistory(coreActivities(state, historyOptions(effective)), effective).map(expose); }
|
|
62
|
+
export async function history(square, query = {}, control) { const { state } = await square.artifact.read(control?.signal); return selectHistory(coreActivities(state, historyOptions(query)), query).map(expose); }
|
|
63
|
+
export async function participantHistory(square, _name, query = {}, control) { const { state } = await square.artifact.read(control?.signal); const effective = query.limit !== undefined ? query : { ...query, limit: 10 }; return selectHistory(coreActivities(state, historyOptions(effective)), effective).map(expose); }
|
|
64
64
|
export async function resolveParticipant(square, name) { const { state } = await square.artifact.read(); return { name: resolveKnownName(state, name), roster: rosterNames(state) }; }
|
|
65
65
|
export async function currentParticipant(square, name) { const { state } = await square.artifact.read(); const known = resolveRosterName(state, name); return known !== undefined && isCurrentlyJoined(state.acts, known) ? known : undefined; }
|
|
66
66
|
export async function participants(square) { const { state } = await square.artifact.read(); return statuses(square, state); }
|
package/dist/wake-attempts.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import os from 'node:os';
|
|
2
|
+
import { hostLedgerRoot } from './host-ledger-root.js';
|
|
2
3
|
import path from 'node:path';
|
|
3
4
|
import { isWakeRouteKind, nameKey } from './model.js';
|
|
4
5
|
import { canonicalSquarePath } from './registry.js';
|
|
@@ -9,7 +10,7 @@ import { redactDiagnostic } from './diagnostic-redaction.js';
|
|
|
9
10
|
export { hasAttemptableWakeRoute, isWakeRouteAttemptable, terminalWakeEvidence } from './square-projections.js';
|
|
10
11
|
const VALID_OUTCOMES = new Set(['accepted', 'unknown', 'failed']);
|
|
11
12
|
function ledger(env) {
|
|
12
|
-
return createHostLedgerPort({
|
|
13
|
+
return createHostLedgerPort({ rootPath: hostLedgerRoot(env) });
|
|
13
14
|
}
|
|
14
15
|
export function wakeAttemptsPath(env = process.env) {
|
|
15
16
|
return env.SQUARE_WAKE_ATTEMPTS || path.join(os.homedir(), '.square', 'wake-attempts.ndjsonl');
|
package/dist/wake-evidence.js
CHANGED
|
@@ -1,17 +1,11 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { hostLedgerRoot } from './host-ledger-root.js';
|
|
2
2
|
import { createHostLedgerPort } from './host-ledger-file-adapter.js';
|
|
3
3
|
import { openSquare } from './square-file-adapter.js';
|
|
4
4
|
import { closeOpenSquare } from './open-square.js';
|
|
5
5
|
import { projectWakeEvidenceFromState, wakeIsEligible, } from './square-projections.js';
|
|
6
6
|
export { wakeIsEligible };
|
|
7
7
|
function hostLedgerForEnv(env) {
|
|
8
|
-
|
|
9
|
-
return createHostLedgerPort({
|
|
10
|
-
userPath: env.SQUARE_HOST_LEDGER_USER ?? root,
|
|
11
|
-
localPath: env.SQUARE_HOST_LEDGER_LOCAL ?? root,
|
|
12
|
-
readableScopes: ['user'],
|
|
13
|
-
writableScope: 'user',
|
|
14
|
-
});
|
|
8
|
+
return createHostLedgerPort({ rootPath: hostLedgerRoot(env) });
|
|
15
9
|
}
|
|
16
10
|
/** Adapter entry: open the artifact, assemble host ports, and project wake evidence. */
|
|
17
11
|
export async function wakeEvidenceProjection(squarePath, now, env) {
|
package/dist/wakes.d.ts
CHANGED
package/dist/wakes.js
CHANGED
|
@@ -21,15 +21,15 @@ export async function pulseWatchLease(square, name, leaseId, options, heartbeatD
|
|
|
21
21
|
const lease = freshWatchLease(state, known, at);
|
|
22
22
|
if (lease?.leaseId !== leaseId)
|
|
23
23
|
return { result: { type: 'replaced' } };
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const terminal = watchTerminalStatus(state, known);
|
|
24
|
+
const held = currentHold(state.acts).active;
|
|
25
|
+
const terminal = held ? undefined : watchTerminalStatus(state, known);
|
|
27
26
|
if (terminal !== undefined)
|
|
28
27
|
return { result: { type: 'terminal', status: terminal } };
|
|
28
|
+
const type = held ? 'held' : 'sleep';
|
|
29
29
|
if (!heartbeatDue)
|
|
30
|
-
return { result: { type
|
|
30
|
+
return { result: { type } };
|
|
31
31
|
writeWatchLease(state, known, { leaseId, heartbeatAt: at, expiresAt: at + WATCH_STALE_MS, ...(Object.keys(filter(options)).length === 0 ? {} : { filter: filter(options) }) });
|
|
32
|
-
return { state, result: { type
|
|
32
|
+
return { state, result: { type, heartbeatAt: at } };
|
|
33
33
|
});
|
|
34
34
|
}
|
|
35
35
|
export async function releaseWatchLease(square, name, leaseId) {
|
package/dist/watch.d.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
1
|
import { type WatchOptions } from './model.js';
|
|
2
|
+
interface WatchCallerContext {
|
|
3
|
+
readonly cwd: string;
|
|
4
|
+
readonly env: NodeJS.ProcessEnv;
|
|
5
|
+
}
|
|
2
6
|
/** `false` is reserved for a quiet --now; idle completion preserves its existing sweep boundary. */
|
|
3
|
-
export declare function cmdWatch(squarePath: string, name: string, opts: WatchOptions): Promise<boolean | undefined>;
|
|
7
|
+
export declare function cmdWatch(squarePath: string, name: string, opts: WatchOptions, caller?: WatchCallerContext): Promise<boolean | undefined>;
|
|
8
|
+
export {};
|
package/dist/watch.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { setTimeout as sleep } from 'node:timers/promises';
|
|
2
1
|
import { isSquareError, } from './model.js';
|
|
3
|
-
import {
|
|
2
|
+
import { STALE_MS, WATCH_HEARTBEAT_MS, inSquareCount, nowMs, } from './runtime.js';
|
|
4
3
|
import { openSquare } from './square-file-adapter.js';
|
|
5
4
|
import { closeOpenSquare } from './open-square.js';
|
|
6
|
-
import {
|
|
5
|
+
import { createSquareApplication } from './square-application.js';
|
|
7
6
|
import { resolveParticipant, watchPresentation } from './views.js';
|
|
8
7
|
import { acquireWatchLease, ownsWatchLease, pulseWatchLease, releaseWatchLease } from './wakes.js';
|
|
9
8
|
import { renderWatchForceTakeover, renderWatchAlreadyActive, renderWatchReplaceMissing, renderWatchOutput, renderWatchReplaced, renderWatchStatus, formatRefusal, participantCommandPrefix, joinRecoveryCommand, participantsRecoveryCommand, withPathOutput, } from './presentation.js';
|
|
@@ -121,11 +120,11 @@ function installWatchInterruptHandler(square, squarePath, name, currentLeaseId)
|
|
|
121
120
|
process.off('SIGINT', onInterrupt);
|
|
122
121
|
};
|
|
123
122
|
}
|
|
124
|
-
async function cmdWatchNow(squarePath, name, opts) {
|
|
125
|
-
const square = await openSquare(squarePath, { clock: nowMs });
|
|
126
|
-
const
|
|
123
|
+
async function cmdWatchNow(squarePath, name, opts, caller) {
|
|
124
|
+
const square = await openSquare(squarePath, { clock: nowMs, env: caller.env });
|
|
125
|
+
const application = createSquareApplication({ cwd: caller.cwd, env: caller.env, squarePath, participant: name, clock: nowMs });
|
|
127
126
|
try {
|
|
128
|
-
const caught = await
|
|
127
|
+
const caught = await application.catch({
|
|
129
128
|
...(opts.id === undefined ? {} : { id: opts.id }),
|
|
130
129
|
...(opts.participants === undefined ? {} : { from: opts.participants }),
|
|
131
130
|
...(opts.mention === undefined ? {} : { mention: true }),
|
|
@@ -140,7 +139,6 @@ async function cmdWatchNow(squarePath, name, opts) {
|
|
|
140
139
|
return caught.activities.length > 0;
|
|
141
140
|
}
|
|
142
141
|
finally {
|
|
143
|
-
await facade.close();
|
|
144
142
|
await closeOpenSquare(square);
|
|
145
143
|
}
|
|
146
144
|
}
|
|
@@ -153,11 +151,11 @@ async function countParticipants(square) {
|
|
|
153
151
|
}
|
|
154
152
|
}
|
|
155
153
|
/** `false` is reserved for a quiet --now; idle completion preserves its existing sweep boundary. */
|
|
156
|
-
export async function cmdWatch(squarePath, name, opts) {
|
|
154
|
+
export async function cmdWatch(squarePath, name, opts, caller = { cwd: process.cwd(), env: { ...process.env } }) {
|
|
157
155
|
let square;
|
|
158
156
|
let participantCount;
|
|
159
157
|
try {
|
|
160
|
-
square = await openSquare(squarePath, { clock: nowMs });
|
|
158
|
+
square = await openSquare(squarePath, { clock: nowMs, env: caller.env });
|
|
161
159
|
participantCount = inSquareCount((await square.artifact.read()).state);
|
|
162
160
|
name = (await resolveParticipant(square, name)).name;
|
|
163
161
|
}
|
|
@@ -194,7 +192,7 @@ export async function cmdWatch(squarePath, name, opts) {
|
|
|
194
192
|
}
|
|
195
193
|
if (opts.now) {
|
|
196
194
|
await closeOpenSquare(square);
|
|
197
|
-
return cmdWatchNow(squarePath, name, opts);
|
|
195
|
+
return cmdWatchNow(squarePath, name, opts, caller);
|
|
198
196
|
}
|
|
199
197
|
const start = await beginWatch(square, squarePath, name, opts);
|
|
200
198
|
if (start.type === 'active') {
|
|
@@ -204,6 +202,7 @@ export async function cmdWatch(squarePath, name, opts) {
|
|
|
204
202
|
process.exit(1);
|
|
205
203
|
}
|
|
206
204
|
let staleSince = nowMs();
|
|
205
|
+
let wasHeld = false;
|
|
207
206
|
let currentLeaseId = start.leaseId;
|
|
208
207
|
let nextHeartbeatAt = start.heartbeatAt + WATCH_HEARTBEAT_MS;
|
|
209
208
|
if (opts.replace) {
|
|
@@ -214,16 +213,18 @@ export async function cmdWatch(squarePath, name, opts) {
|
|
|
214
213
|
}
|
|
215
214
|
const idleMs = opts.idleMs ?? STALE_MS;
|
|
216
215
|
const removeInterruptHandler = installWatchInterruptHandler(square, squarePath, name, () => currentLeaseId);
|
|
217
|
-
const
|
|
216
|
+
const application = createSquareApplication({ cwd: caller.cwd, env: caller.env, squarePath, participant: name, clock: nowMs });
|
|
218
217
|
try {
|
|
219
218
|
while (true) {
|
|
219
|
+
// Capture before evaluation: changes during catch/lease checks must not be lost.
|
|
220
|
+
const baseline = (await square.artifact.read()).version;
|
|
220
221
|
const leaseState = await pulseWatchLease(square, name, currentLeaseId, opts, nowMs() >= nextHeartbeatAt);
|
|
221
|
-
if (leaseState.type === 'sleep' && leaseState.heartbeatAt !== undefined) {
|
|
222
|
+
if ((leaseState.type === 'sleep' || leaseState.type === 'held') && leaseState.heartbeatAt !== undefined) {
|
|
222
223
|
nextHeartbeatAt = leaseState.heartbeatAt + WATCH_HEARTBEAT_MS;
|
|
223
224
|
}
|
|
224
225
|
let result = leaseState;
|
|
225
226
|
if (result.type === 'sleep') {
|
|
226
|
-
const caught = await
|
|
227
|
+
const caught = await application.catch({
|
|
227
228
|
...(opts.participants === undefined ? {} : { from: opts.participants }),
|
|
228
229
|
...(opts.mention === undefined ? {} : { mention: true }),
|
|
229
230
|
...(opts.limit === undefined ? {} : { limit: opts.limit }),
|
|
@@ -236,9 +237,10 @@ export async function cmdWatch(squarePath, name, opts) {
|
|
|
236
237
|
currentLeaseId = undefined;
|
|
237
238
|
return;
|
|
238
239
|
}
|
|
239
|
-
if (result.type === 'held')
|
|
240
|
+
if (result.type === 'held' || wasHeld)
|
|
240
241
|
staleSince = nowMs();
|
|
241
|
-
|
|
242
|
+
wasHeld = result.type === 'held';
|
|
243
|
+
if (!wasHeld && nowMs() - staleSince >= idleMs) {
|
|
242
244
|
const result = !await ownsWatchLease(square, name, currentLeaseId)
|
|
243
245
|
? { type: 'replaced' }
|
|
244
246
|
: { type: 'terminal', status: 'stale' };
|
|
@@ -247,11 +249,11 @@ export async function cmdWatch(squarePath, name, opts) {
|
|
|
247
249
|
return;
|
|
248
250
|
}
|
|
249
251
|
}
|
|
250
|
-
|
|
252
|
+
const nextDeadline = Math.min(nextHeartbeatAt, wasHeld ? Infinity : staleSince + idleMs);
|
|
253
|
+
await square.artifact.changed(baseline, Math.max(0, nextDeadline - nowMs()));
|
|
251
254
|
}
|
|
252
255
|
}
|
|
253
256
|
finally {
|
|
254
|
-
await facade.close();
|
|
255
257
|
await closeOpenSquare(square);
|
|
256
258
|
removeInterruptHandler();
|
|
257
259
|
}
|