@astrosheep/square 0.3.11 → 0.3.13
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/codex-plugin/.codex-plugin/plugin.json +1 -1
- package/codex-plugin/hooks/hooks.json +9 -0
- package/dist/activity-feed.d.ts +14 -0
- package/dist/activity-feed.js +5 -5
- package/dist/activity.d.ts +9 -0
- package/dist/activity.js +112 -96
- package/dist/artifact.d.ts +20 -0
- package/dist/artifact.js +317 -648
- package/dist/automatic-session.d.ts +5 -0
- package/dist/automatic-session.js +86 -0
- package/dist/boundary-presentation.d.ts +5 -0
- package/dist/boundary-presentation.js +6 -5
- package/dist/claude-hook.d.ts +9 -0
- package/dist/claude-hook.js +35 -3
- package/dist/cli/context.d.ts +35 -0
- package/dist/cli/context.js +26 -31
- package/dist/cli/harness-command.d.ts +28 -0
- package/dist/cli/harness-command.js +30 -15
- package/dist/cli/maintenance-commands.d.ts +7 -0
- package/dist/cli/maintenance-commands.js +12 -69
- package/dist/cli/meta-commands.d.ts +5 -0
- package/dist/cli/observation-commands.d.ts +20 -0
- package/dist/cli/observation-commands.js +159 -139
- package/dist/cli/program.d.ts +2 -0
- package/dist/cli/program.js +3 -12
- package/dist/cli/registry.d.ts +9 -0
- package/dist/cli/registry.js +2 -4
- package/dist/cli/square-commands.d.ts +34 -0
- package/dist/cli/square-commands.js +90 -84
- package/dist/cmd/notify-once.d.ts +2 -0
- package/dist/codex-hook.d.ts +10 -0
- package/dist/codex-hook.js +35 -3
- package/dist/decisions.d.ts +92 -0
- package/dist/decisions.js +93 -81
- package/dist/delivery-health.d.ts +21 -0
- package/dist/delivery-health.js +41 -34
- package/dist/delivery.d.ts +81 -0
- package/dist/delivery.js +71 -69
- package/dist/file-lock.d.ts +6 -0
- package/dist/harness-claude.d.ts +19 -0
- package/dist/harness-claude.js +8 -3
- package/dist/harness-codex.d.ts +29 -0
- package/dist/harness-codex.js +2 -2
- package/dist/harness-links.d.ts +17 -0
- package/dist/harness-links.js +2 -2
- package/dist/harness-pi.d.ts +11 -0
- package/dist/harness-pi.js +2 -2
- package/dist/harness-stage.d.ts +6 -0
- package/dist/harness.d.ts +20 -0
- package/dist/harness.js +5 -3
- package/dist/help.d.ts +6 -0
- package/dist/help.js +12 -13
- package/dist/identity.d.ts +13 -0
- package/dist/inbox.d.ts +2 -0
- package/dist/inbox.js +15 -19
- package/dist/index.d.ts +13 -0
- package/dist/index.js +31 -67
- package/dist/landing.d.ts +10 -0
- package/dist/landing.js +95 -0
- package/dist/list.d.ts +1 -0
- package/dist/list.js +39 -61
- package/dist/model.d.ts +147 -0
- package/dist/model.js +12 -8
- package/dist/notifications.d.ts +30 -0
- package/dist/notifications.js +83 -91
- package/dist/open-square.d.ts +11 -0
- package/dist/open-square.js +4 -0
- package/dist/participant-identity.d.ts +1 -0
- package/dist/participant-identity.js +3 -0
- package/dist/paseo-connection.d.ts +15 -0
- package/dist/paseo-delivery.d.ts +14 -0
- package/dist/paseo-state.d.ts +11 -0
- package/dist/paseo-timeline.d.ts +16 -0
- package/dist/presence.d.ts +3 -0
- package/dist/presence.js +42 -0
- package/dist/presentation.d.ts +91 -0
- package/dist/presentation.js +52 -63
- package/dist/presented.d.ts +11 -0
- package/dist/registry.d.ts +58 -0
- package/dist/registry.js +36 -3
- package/dist/routes.d.ts +42 -0
- package/dist/runtime.d.ts +45 -0
- package/dist/runtime.js +48 -55
- package/dist/search.d.ts +11 -0
- package/dist/square-core.d.ts +111 -0
- package/dist/square-core.js +63 -9
- package/dist/square-facade.d.ts +86 -0
- package/dist/square-facade.js +1 -0
- package/dist/square-file-adapter.d.ts +18 -0
- package/dist/square-file-adapter.js +70 -0
- package/dist/square-storage.d.ts +20 -0
- package/dist/square-storage.js +171 -0
- package/dist/square-wiring.d.ts +19 -0
- package/dist/square-wiring.js +70 -0
- package/dist/square.d.ts +2 -0
- package/dist/state-cell.d.ts +14 -0
- package/dist/state-cell.js +1 -0
- package/dist/stream.d.ts +3 -0
- package/dist/stream.js +19 -23
- package/dist/time.d.ts +6 -0
- package/dist/views.d.ts +95 -0
- package/dist/views.js +81 -0
- package/dist/wake-attempts.d.ts +39 -0
- package/dist/wake-attempts.js +8 -4
- package/dist/wake-evidence.d.ts +12 -0
- package/dist/wake-evidence.js +7 -10
- package/dist/wake-port.d.ts +20 -0
- package/dist/wake-sink.d.ts +12 -0
- package/dist/wakes.d.ts +40 -0
- package/dist/wakes.js +62 -0
- package/dist/watch.d.ts +2 -0
- package/dist/watch.js +89 -210
- package/extensions/square-opencode.js +27 -1
- package/extensions/square-pi.js +13 -0
- package/guides/participant.md +7 -174
- package/package.json +5 -4
- package/skills/brainstorm/SKILL.md +30 -30
- package/skills/square/.claude-plugin/plugin.json +1 -1
- package/skills/square/SKILL.md +56 -29
- package/skills/square/hooks/hooks.json +9 -0
- package/skills/square-feedback/SKILL.md +9 -9
- package/dist/compact.js +0 -26
- package/dist/doctor.js +0 -36
- package/dist/square-application.js +0 -262
- package/template.md +0 -4
- package/templates/architect.md +0 -4
- package/templates/brainstorm.md +0 -4
package/dist/presentation.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { sameName } from './model.js';
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
-
import {
|
|
5
|
-
|
|
4
|
+
import { participantIdentity } from './participant-identity.js';
|
|
5
|
+
export { participantIdentity } from './participant-identity.js';
|
|
6
|
+
import { audienceIncludes, audienceOf, perceive } from './square-core.js';
|
|
7
|
+
import { actId, publicActs, readCursor, rosterNames, sayNumberFor } from './runtime.js';
|
|
6
8
|
import { formatDuration, formatRelativeTime, formatTimestamp } from './time.js';
|
|
7
9
|
import { grepSnippet } from './search.js';
|
|
8
10
|
function headerLine(squarePath, opts = {}) {
|
|
@@ -49,7 +51,7 @@ function pluralize(count, singular, plural = `${singular}s`) {
|
|
|
49
51
|
const PRESENCE_WINDOW_MS = 8 * 60 * 60 * 1000;
|
|
50
52
|
function presenceGlyph(participant) {
|
|
51
53
|
if (participant.state === 'done')
|
|
52
|
-
return '
|
|
54
|
+
return '○';
|
|
53
55
|
if (participant.presence === 'watching')
|
|
54
56
|
return '◎';
|
|
55
57
|
if (participant.presenceAt !== undefined)
|
|
@@ -77,7 +79,7 @@ export function renderPresenceLines(participants, now, limit = 5) {
|
|
|
77
79
|
const shown = recent.slice(0, limit);
|
|
78
80
|
if (shown.length === 0)
|
|
79
81
|
return [' ○ nobody nearby'];
|
|
80
|
-
const lines = shown.map((p) => ` ${presenceGlyph(p)} ${p.name} · ${presenceText(p, now)}`);
|
|
82
|
+
const lines = shown.map((p) => ` ${presenceGlyph(p)} ${participantIdentity(p.name)} · ${presenceText(p, now)}`);
|
|
81
83
|
const remaining = recent.length - shown.length;
|
|
82
84
|
if (remaining > 0)
|
|
83
85
|
lines.push(` ○ …and ${remaining} more`);
|
|
@@ -116,15 +118,16 @@ export function previewActivityBody(body) {
|
|
|
116
118
|
}
|
|
117
119
|
export function renderRoomChangeText(event) {
|
|
118
120
|
const actor = event.actor ?? 'someone';
|
|
121
|
+
const identity = actor === 'someone' ? actor : participantIdentity(actor);
|
|
119
122
|
switch (event.kind) {
|
|
120
123
|
case 'join':
|
|
121
|
-
return `${
|
|
124
|
+
return `${identity} stepped into the square`;
|
|
122
125
|
case 'done':
|
|
123
|
-
return `${
|
|
126
|
+
return `${identity} stepped out of the square`;
|
|
124
127
|
case 'hold':
|
|
125
|
-
return `${
|
|
128
|
+
return `${identity} raised a hand${event.body ? ` — ${event.body}` : ''}`;
|
|
126
129
|
case 'resume':
|
|
127
|
-
return `${
|
|
130
|
+
return `${identity} lowered the hand`;
|
|
128
131
|
}
|
|
129
132
|
}
|
|
130
133
|
function renderedBody(body, maxChars) {
|
|
@@ -150,38 +153,32 @@ export function renderEventCli(event, opts = {}) {
|
|
|
150
153
|
case 'say': {
|
|
151
154
|
const body = renderedBody(event.body, maxBody);
|
|
152
155
|
const mention = opts.mention;
|
|
153
|
-
const mentionSuffix = mention !== undefined &&
|
|
154
|
-
? ` · calls your name across the square —
|
|
156
|
+
const mentionSuffix = mention !== undefined && audienceIncludes(audienceOf(event), mention)
|
|
157
|
+
? ` · calls your name across the square — ${participantIdentity(mention)}`
|
|
155
158
|
: '';
|
|
156
159
|
const replySuffix = event.reply === undefined ? '' : ` · replies to ${actId(event.reply)}`;
|
|
157
|
-
return `● ${event.actor} #${opts.actNumber ?? 1} · ${actId(event)} · ${formatRelativeTime(event.at, now)}${mentionSuffix}${replySuffix}${bodySuffix(body)}`;
|
|
160
|
+
return `● ${participantIdentity(event.actor)} #${opts.actNumber ?? 1} · ${actId(event)} · ${formatRelativeTime(event.at, now)}${mentionSuffix}${replySuffix}${bodySuffix(body)}`;
|
|
158
161
|
}
|
|
159
162
|
case 'done': {
|
|
160
163
|
const body = renderedBody(event.body, maxBody);
|
|
161
|
-
return
|
|
164
|
+
return `○ ${participantIdentity(event.actor)} stepped out of the square — done · ${actId(event)} · ${formatRelativeTime(event.at, now)}${bodySuffix(body)}`;
|
|
162
165
|
}
|
|
163
166
|
case 'read':
|
|
164
167
|
return '';
|
|
165
168
|
}
|
|
166
169
|
}
|
|
167
|
-
function renderPresenceOnlySay(event) {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
}
|
|
172
|
-
function perceptionFor(history, event, viewer) {
|
|
173
|
-
const cutoff = history.findIndex((item) => actStableIndex(item) === actStableIndex(event));
|
|
174
|
-
const acts = cutoff >= 0 ? history.slice(0, cutoff) : history;
|
|
175
|
-
return perceive(fold(acts), event, viewer);
|
|
170
|
+
function renderPresenceOnlySay(event, opts = {}) {
|
|
171
|
+
const audience = audienceOf(event);
|
|
172
|
+
const targets = audience.kind === 'bell' ? [] : audience.names;
|
|
173
|
+
const dest = targets.length === 0 ? '' : ` ${targets.map((name) => participantIdentity(name)).join(' and ')}`;
|
|
174
|
+
return `● ${participantIdentity(event.actor)} #${opts.actNumber ?? 1} · ${actId(event)} · ${formatRelativeTime(event.at, opts.now)}\n talked to${dest}`;
|
|
176
175
|
}
|
|
177
|
-
export function
|
|
176
|
+
export function renderAmbientEvent(event, viewer, opts = {}) {
|
|
178
177
|
if (event.kind !== 'say')
|
|
179
178
|
return renderEventCli(event, opts);
|
|
180
|
-
const seen =
|
|
181
|
-
if (seen === 'none')
|
|
182
|
-
return '';
|
|
179
|
+
const seen = perceive(event, viewer);
|
|
183
180
|
if (seen === 'presence')
|
|
184
|
-
return renderPresenceOnlySay(event);
|
|
181
|
+
return renderPresenceOnlySay(event, opts);
|
|
185
182
|
return renderEventCli(event, opts);
|
|
186
183
|
}
|
|
187
184
|
function draftSavedLines(draftPath) {
|
|
@@ -194,12 +191,13 @@ function renderUnreadSummary(opts) {
|
|
|
194
191
|
return [
|
|
195
192
|
...opts.activitySummaries.flatMap((item) => [
|
|
196
193
|
...item.previews.slice(-1).map((preview) => {
|
|
197
|
-
const rendered =
|
|
194
|
+
const rendered = renderAmbientEvent(preview.act, opts.viewer, { actNumber: preview.number });
|
|
198
195
|
if (rendered === '')
|
|
199
|
-
return ` · ${item.name} spoke — ${formatAge(item.latestActivityAgeMs)} ago`;
|
|
200
|
-
if (
|
|
201
|
-
return ` · ${item.name} spoke — ${formatAge(item.latestActivityAgeMs)} ago · ${rendered}`;
|
|
202
|
-
|
|
196
|
+
return ` · ${participantIdentity(item.name)} spoke — ${formatAge(item.latestActivityAgeMs)} ago`;
|
|
197
|
+
if (preview.act.kind === 'say' && perceive(preview.act, opts.viewer) === 'presence') {
|
|
198
|
+
return ` · ${participantIdentity(item.name)} spoke — ${formatAge(item.latestActivityAgeMs)} ago · ${rendered.replace(/\n/g, ' ')}`;
|
|
199
|
+
}
|
|
200
|
+
return ` · ${participantIdentity(item.name)} spoke — ${formatAge(item.latestActivityAgeMs)} ago · "${previewActivityBody(preview.act.body)}"`;
|
|
203
201
|
}),
|
|
204
202
|
]),
|
|
205
203
|
...opts.roomChanges.map((act) => ` · ${renderRoomChangeText(act)}`),
|
|
@@ -208,7 +206,7 @@ function renderUnreadSummary(opts) {
|
|
|
208
206
|
export function renderPendingFeed(history, publicItems, roomChanges, viewer = '') {
|
|
209
207
|
const lines = [];
|
|
210
208
|
for (const act of publicItems) {
|
|
211
|
-
const rendered =
|
|
209
|
+
const rendered = renderAmbientEvent(act, viewer, {
|
|
212
210
|
actNumber: act.kind === 'say' ? sayNumberFor(history, act) : undefined,
|
|
213
211
|
});
|
|
214
212
|
if (rendered !== '')
|
|
@@ -262,15 +260,15 @@ export function renderPublicTail(events, lastN, now, viewer = '') {
|
|
|
262
260
|
const selected = lastN == null ? publicItems : publicItems.slice(-lastN);
|
|
263
261
|
const preview = lastN == null ? undefined : BODY_PREVIEW_LENGTH;
|
|
264
262
|
return selected
|
|
265
|
-
.map((event) =>
|
|
263
|
+
.map((event) => renderAmbientEvent(event, viewer, { now, preview, actNumber: event.kind === 'say' ? sayNumberFor(events, event) : undefined }))
|
|
266
264
|
.filter(Boolean)
|
|
267
265
|
.join('\n\n');
|
|
268
266
|
}
|
|
269
|
-
function lastPresenceAnchor(
|
|
270
|
-
const cursor = readCursor(
|
|
271
|
-
for (let i =
|
|
272
|
-
const event =
|
|
273
|
-
const index =
|
|
267
|
+
function lastPresenceAnchor(squareState, name) {
|
|
268
|
+
const cursor = readCursor(squareState, name);
|
|
269
|
+
for (let i = squareState.acts.length - 1; i >= 0; i--) {
|
|
270
|
+
const event = squareState.acts[i];
|
|
271
|
+
const index = event.index;
|
|
274
272
|
if (index > cursor)
|
|
275
273
|
continue;
|
|
276
274
|
if (event.kind === 'say' || event.kind === 'done')
|
|
@@ -279,24 +277,27 @@ function lastPresenceAnchor(doc, name) {
|
|
|
279
277
|
return -1;
|
|
280
278
|
}
|
|
281
279
|
function renderLastPresenceMarker(name) {
|
|
282
|
-
return
|
|
280
|
+
return `→ ${participantIdentity(name)} was here`;
|
|
283
281
|
}
|
|
284
|
-
export function renderActivitiesView(
|
|
282
|
+
export function renderActivitiesView(squareState, visible, lastN, full, squarePath, viewer = '', mode = 'ambient') {
|
|
285
283
|
const publicVisible = visible.filter((act) => act.kind === 'say' || act.kind === 'done');
|
|
286
284
|
const shown = lastN == null ? publicVisible : publicVisible.slice(-lastN);
|
|
287
285
|
const previewLen = full ? undefined : BODY_PREVIEW_LENGTH;
|
|
288
286
|
const markers = new Map();
|
|
289
|
-
for (const participant of rosterNames(
|
|
290
|
-
const anchor = lastPresenceAnchor(
|
|
287
|
+
for (const participant of rosterNames(squareState)) {
|
|
288
|
+
const anchor = lastPresenceAnchor(squareState, participant);
|
|
291
289
|
if (anchor >= 0)
|
|
292
290
|
markers.set(anchor, [...(markers.get(anchor) ?? []), participant]);
|
|
293
291
|
}
|
|
294
292
|
const chunks = [];
|
|
295
293
|
for (const act of shown) {
|
|
296
|
-
const
|
|
294
|
+
const opts = {
|
|
297
295
|
preview: previewLen,
|
|
298
|
-
actNumber: act.kind === 'say' ? sayNumberFor(
|
|
299
|
-
}
|
|
296
|
+
actNumber: act.kind === 'say' ? sayNumberFor(squareState.acts, act) : undefined,
|
|
297
|
+
};
|
|
298
|
+
const rendered = mode === 'archive'
|
|
299
|
+
? renderEventCli(act, opts)
|
|
300
|
+
: renderAmbientEvent(act, viewer, opts);
|
|
300
301
|
if (rendered !== '')
|
|
301
302
|
chunks.push(rendered);
|
|
302
303
|
for (const participant of markers.get(act.index) ?? []) {
|
|
@@ -306,7 +307,7 @@ export function renderActivitiesView(doc, visible, lastN, full, squarePath, view
|
|
|
306
307
|
if (chunks.length === 0)
|
|
307
308
|
return 'latest\n ○ no public activity in this view';
|
|
308
309
|
if (previewLen !== undefined) {
|
|
309
|
-
const truncated = shown.some((act) => act.kind === 'say' && act.body.length > previewLen);
|
|
310
|
+
const truncated = shown.some((act) => act.kind === 'say' && act.body.length > previewLen && (mode === 'archive' || perceive(act, viewer) === 'full'));
|
|
310
311
|
if (truncated)
|
|
311
312
|
chunks.push(`» ${commandPrefix(squarePath)} history --full`);
|
|
312
313
|
}
|
|
@@ -329,13 +330,13 @@ export function renderGrepActivitiesView(visible, totalMatches, full, squarePath
|
|
|
329
330
|
const rawBody = act.body ?? '';
|
|
330
331
|
if (full === true) {
|
|
331
332
|
const body = rawBody.split('\n').map((line) => ` ${line}`).join('\n');
|
|
332
|
-
chunks.push(`${actId(act.index)} · ${act.actor
|
|
333
|
+
chunks.push(`${actId(act.index)} · ${act.actor === undefined ? 'unknown' : participantIdentity(act.actor)} · ${formatTimestamp(act.at)}\n${body}`);
|
|
333
334
|
continue;
|
|
334
335
|
}
|
|
335
336
|
const snippet = grepSnippet(rawBody, pattern, GREP_PREVIEW_CHARS, fixed);
|
|
336
337
|
if (snippet === undefined) {
|
|
337
338
|
const preview = previewBody(rawBody, GREP_PREVIEW_CHARS);
|
|
338
|
-
chunks.push(`${actId(act.index)} · ${act.actor
|
|
339
|
+
chunks.push(`${actId(act.index)} · ${act.actor === undefined ? 'unknown' : participantIdentity(act.actor)} · ${formatTimestamp(act.at)}${preview === '' ? '' : `\n ${preview}`}`);
|
|
339
340
|
continue;
|
|
340
341
|
}
|
|
341
342
|
const clippedBefore = snippet.beforeOmitted > 0;
|
|
@@ -345,7 +346,7 @@ export function renderGrepActivitiesView(visible, totalMatches, full, squarePath
|
|
|
345
346
|
const omitted = clippedBefore || clippedAfter
|
|
346
347
|
? `\n · ${snippet.beforeOmitted} chars before · ${snippet.afterOmitted} chars after`
|
|
347
348
|
: '';
|
|
348
|
-
chunks.push(`${actId(act.index)} · ${act.actor
|
|
349
|
+
chunks.push(`${actId(act.index)} · ${act.actor === undefined ? 'unknown' : participantIdentity(act.actor)} · ${formatTimestamp(act.at)}\n ${text.trim()}${omitted}`);
|
|
349
350
|
}
|
|
350
351
|
if (publicVisible.length === 1) {
|
|
351
352
|
chunks.push(`» ${commandPrefix(squarePath)} history --at ${actId(publicVisible[0].index)} -C 2${truncated ? ' --full' : ''}`);
|
|
@@ -412,20 +413,8 @@ function renderRoomChanges(changes) {
|
|
|
412
413
|
export function renderDoctorClean() {
|
|
413
414
|
return '✓ no problems found';
|
|
414
415
|
}
|
|
415
|
-
export function renderDoctorProblems(problems) {
|
|
416
|
-
return [`✕ ${problems.length} ${pluralize(problems.length, 'problem')} found`, ...problems.map((problem) => ` · ${problem.kind}: ${problem.message}`)].join('\n');
|
|
417
|
-
}
|
|
418
416
|
export function renderDoctorUnfixable(reason) {
|
|
419
|
-
return ['✕
|
|
420
|
-
}
|
|
421
|
-
export function renderDoctorRepaired(actions, quarantinedCount, sidecarPath) {
|
|
422
|
-
if (actions.length === 0)
|
|
423
|
-
return '✓ no problems found';
|
|
424
|
-
return [
|
|
425
|
-
'✓ repaired',
|
|
426
|
-
...actions.map((action) => ` · ${action.message}`),
|
|
427
|
-
...(quarantinedCount > 0 && sidecarPath !== undefined ? [` · quarantined ${quarantinedCount} act block(s)`, ` · sidecar ${sidecarPath}`] : []),
|
|
428
|
-
].join('\n');
|
|
417
|
+
return ['✕ unreadable artifact', ` · ${reason}`].join('\n');
|
|
429
418
|
}
|
|
430
419
|
export function renderWatchOutput(history, publicItems, roomChanges, opts) {
|
|
431
420
|
const sections = [];
|
|
@@ -448,7 +437,7 @@ export function renderWatchOutput(history, publicItems, roomChanges, opts) {
|
|
|
448
437
|
sections.push(room);
|
|
449
438
|
if (publicItems.length > 0) {
|
|
450
439
|
const rendered = publicItems
|
|
451
|
-
.map((act) =>
|
|
440
|
+
.map((act) => renderAmbientEvent(act, opts.viewer, {
|
|
452
441
|
actNumber: act.kind === 'say' ? sayNumberFor(history, act) : undefined,
|
|
453
442
|
mention: opts.mention,
|
|
454
443
|
}))
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type InboxMembership } from './model.js';
|
|
2
|
+
export declare function presentedPath(env?: NodeJS.ProcessEnv): string;
|
|
3
|
+
export declare function hasPresentedForOwner(ownerId: string, squarePath: string, name: string, actIndex: number, env?: NodeJS.ProcessEnv, now?: number): boolean;
|
|
4
|
+
/** True when any current participant owner has already received this attention. */
|
|
5
|
+
export declare function hasPresentedAttention(squarePath: string, name: string, actIndex: number, env?: NodeJS.ProcessEnv, now?: number): boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Serialize presentation only for the affected participants. Delivery runs
|
|
8
|
+
* outside the short ledger-write lock, so unrelated owners never wait on an
|
|
9
|
+
* adapter. A throwing callback leaves no row and remains unpresented.
|
|
10
|
+
*/
|
|
11
|
+
export declare function presentOnce<T>(sessionId: string, lookup: (sessionId: string) => InboxMembership[], deliver: (inbox: InboxMembership[]) => T, env?: NodeJS.ProcessEnv, at?: number): T | undefined;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Machine-local participant discovery cache.
|
|
3
|
+
*
|
|
4
|
+
* The Square artifact remains authoritative for membership. This append-only
|
|
5
|
+
* cache only maps native harness sessions and optional Paseo agent ids back to
|
|
6
|
+
* active (square path, participant name) pairs.
|
|
7
|
+
*/
|
|
8
|
+
import { type StoredAct } from './model.js';
|
|
9
|
+
export type SessionChannel = 'claude-code' | 'codex' | 'opencode' | 'pi' | 'paseo' | 'unknown';
|
|
10
|
+
export interface RegistryBinding {
|
|
11
|
+
sessionId: string;
|
|
12
|
+
name: string;
|
|
13
|
+
squarePath: string;
|
|
14
|
+
channel: SessionChannel;
|
|
15
|
+
child: boolean;
|
|
16
|
+
paseoAgentId?: string;
|
|
17
|
+
ownerId: string;
|
|
18
|
+
updatedAt: number;
|
|
19
|
+
}
|
|
20
|
+
export interface RegistryWriteOptions {
|
|
21
|
+
channel?: SessionChannel;
|
|
22
|
+
child?: boolean;
|
|
23
|
+
paseoAgentId?: string;
|
|
24
|
+
ownerId?: string;
|
|
25
|
+
at?: number;
|
|
26
|
+
}
|
|
27
|
+
export declare function registryPath(): string;
|
|
28
|
+
export declare function canonicalSquarePath(squarePath: string): string;
|
|
29
|
+
export declare function recordJoin(sessionId: string, name: string, squarePath: string, options?: RegistryWriteOptions): void;
|
|
30
|
+
export declare function recordDone(sessionId: string, name: string, squarePath: string, options?: RegistryWriteOptions): void;
|
|
31
|
+
export declare function lookupSessionBindings(sessionId: string, now?: number): RegistryBinding[];
|
|
32
|
+
export declare function lookupSession(sessionId: string, now?: number): Array<{
|
|
33
|
+
name: string;
|
|
34
|
+
squarePath: string;
|
|
35
|
+
}>;
|
|
36
|
+
export declare function lookupParticipant(squarePath: string, name: string, now?: number): RegistryBinding[];
|
|
37
|
+
/** Resolve the current local harness owner for a participant, if one is registered. */
|
|
38
|
+
export declare function localParticipantOwner(squarePath: string, name: string, env?: NodeJS.ProcessEnv, now?: number): string | undefined;
|
|
39
|
+
export declare function localParticipantName(squarePath: string, env?: NodeJS.ProcessEnv): string | undefined;
|
|
40
|
+
export interface RegistryPruneResult {
|
|
41
|
+
removed: number;
|
|
42
|
+
kept: number;
|
|
43
|
+
}
|
|
44
|
+
/** Compact the registry and remove only bindings disproved by their authoritative artifact. */
|
|
45
|
+
export declare function pruneRegistry(readActs: (squarePath: string) => StoredAct[] | undefined, now?: number): RegistryPruneResult;
|
|
46
|
+
export interface LocalSessionIdentity {
|
|
47
|
+
sessionId: string;
|
|
48
|
+
channel: SessionChannel;
|
|
49
|
+
child: boolean;
|
|
50
|
+
paseoAgentId?: string;
|
|
51
|
+
}
|
|
52
|
+
export declare function localSessionIdentities(env?: NodeJS.ProcessEnv): LocalSessionIdentity[];
|
|
53
|
+
/** True when this process belongs to a harness that can deliver Square attention without a foreground catch. */
|
|
54
|
+
export declare function hasAutomaticDeliveryIdentity(env?: NodeJS.ProcessEnv): boolean;
|
|
55
|
+
export declare function recordLocalJoin(name: string, squarePath: string, env?: NodeJS.ProcessEnv): void;
|
|
56
|
+
export declare function recordLocalDone(name: string, squarePath: string, env?: NodeJS.ProcessEnv): void;
|
|
57
|
+
export declare function recordSessionJoin(sessionId: string, name: string, squarePath: string, channel: SessionChannel, env?: NodeJS.ProcessEnv): string;
|
|
58
|
+
export declare function recordSessionDone(sessionId: string, name: string, squarePath: string, channel: SessionChannel, env?: NodeJS.ProcessEnv): boolean;
|
package/dist/registry.js
CHANGED
|
@@ -36,8 +36,8 @@ export function canonicalSquarePath(squarePath) {
|
|
|
36
36
|
return absolute;
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
|
-
function bindingKey(sessionId, squarePath, name) {
|
|
40
|
-
return JSON.stringify([sessionId, canonicalSquarePath(squarePath), nameKey(name)]);
|
|
39
|
+
function bindingKey(sessionId, squarePath, name, channel) {
|
|
40
|
+
return JSON.stringify([sessionId, canonicalSquarePath(squarePath), nameKey(name), channel]);
|
|
41
41
|
}
|
|
42
42
|
function participantKey(squarePath, name) {
|
|
43
43
|
return JSON.stringify([canonicalSquarePath(squarePath), nameKey(name)]);
|
|
@@ -94,7 +94,7 @@ function foldRegistry(raw, now) {
|
|
|
94
94
|
continue;
|
|
95
95
|
order++;
|
|
96
96
|
const ownerId = entry.owner_id ?? `legacy:${order}`;
|
|
97
|
-
state.set(bindingKey(entry.session_id, entry.square_path, entry.name), {
|
|
97
|
+
state.set(bindingKey(entry.session_id, entry.square_path, entry.name, entry.channel), {
|
|
98
98
|
entry,
|
|
99
99
|
updatedAt: Date.parse(entry.ts),
|
|
100
100
|
ownerId,
|
|
@@ -231,6 +231,11 @@ export function localParticipantOwner(squarePath, name, env = process.env, now =
|
|
|
231
231
|
return undefined;
|
|
232
232
|
return lookupParticipant(squarePath, name, now).find((binding) => sessionIds.has(binding.sessionId))?.ownerId;
|
|
233
233
|
}
|
|
234
|
+
export function localParticipantName(squarePath, env = process.env) {
|
|
235
|
+
const identities = localSessionIdentities(env);
|
|
236
|
+
const names = new Set(identities.flatMap((identity) => lookupSession(identity.sessionId).filter((item) => canonicalSquarePath(item.squarePath) === canonicalSquarePath(squarePath)).map((item) => item.name)));
|
|
237
|
+
return names.size === 1 ? [...names][0] : undefined;
|
|
238
|
+
}
|
|
234
239
|
function bindingIsProvablyObsolete(binding, acts) {
|
|
235
240
|
return acts !== undefined && !isCurrentlyJoined(acts, binding.name);
|
|
236
241
|
}
|
|
@@ -306,3 +311,31 @@ export function recordLocalDone(name, squarePath, env = process.env) {
|
|
|
306
311
|
retireOwnerWakeRoutes(ownerId, { at, env });
|
|
307
312
|
}
|
|
308
313
|
}
|
|
314
|
+
export function recordSessionJoin(sessionId, name, squarePath, channel, env = process.env) {
|
|
315
|
+
const at = Date.now();
|
|
316
|
+
const ownerId = nextOwnerId();
|
|
317
|
+
const current = lookupParticipant(squarePath, name, at);
|
|
318
|
+
for (const binding of current) {
|
|
319
|
+
recordDone(binding.sessionId, binding.name, binding.squarePath, {
|
|
320
|
+
channel: binding.channel,
|
|
321
|
+
child: binding.child,
|
|
322
|
+
...(binding.paseoAgentId ? { paseoAgentId: binding.paseoAgentId } : {}),
|
|
323
|
+
at,
|
|
324
|
+
});
|
|
325
|
+
retireOwnerWakeRoutes(binding.ownerId, { at, env });
|
|
326
|
+
}
|
|
327
|
+
recordJoin(sessionId, name, squarePath, { channel, at, ownerId });
|
|
328
|
+
publishWakeRoutes(ownerId, { at, env });
|
|
329
|
+
return ownerId;
|
|
330
|
+
}
|
|
331
|
+
export function recordSessionDone(sessionId, name, squarePath, channel, env = process.env) {
|
|
332
|
+
const binding = lookupSessionBindings(sessionId).find((item) => canonicalSquarePath(item.squarePath) === canonicalSquarePath(squarePath)
|
|
333
|
+
&& sameName(item.name, name)
|
|
334
|
+
&& item.channel === channel);
|
|
335
|
+
if (binding === undefined)
|
|
336
|
+
return false;
|
|
337
|
+
const at = Date.now();
|
|
338
|
+
recordDone(sessionId, binding.name, binding.squarePath, { channel, at });
|
|
339
|
+
retireOwnerWakeRoutes(binding.ownerId, { at, env });
|
|
340
|
+
return true;
|
|
341
|
+
}
|
package/dist/routes.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { type WakeRoute, type WakeRouteKind } from './model.js';
|
|
2
|
+
export { isWakeRouteKind, WAKE_ROUTE_KINDS } from './model.js';
|
|
3
|
+
export type { WakeRoute, WakeRouteKind } from './model.js';
|
|
4
|
+
export declare const ROUTE_FRESH_MS: number;
|
|
5
|
+
export declare function routesPath(env?: NodeJS.ProcessEnv): string;
|
|
6
|
+
export declare function readWakeRoutes(opts?: {
|
|
7
|
+
ownerId?: string;
|
|
8
|
+
freshOnly?: boolean;
|
|
9
|
+
now?: number;
|
|
10
|
+
env?: NodeJS.ProcessEnv;
|
|
11
|
+
}): WakeRoute[];
|
|
12
|
+
export declare function upsertWakeRoute(route: Omit<WakeRoute, 'updatedAt'>, opts?: {
|
|
13
|
+
at?: number;
|
|
14
|
+
env?: NodeJS.ProcessEnv;
|
|
15
|
+
}): void;
|
|
16
|
+
export declare function retireOwnerWakeRoutes(ownerId: string, opts?: {
|
|
17
|
+
at?: number;
|
|
18
|
+
env?: NodeJS.ProcessEnv;
|
|
19
|
+
}): void;
|
|
20
|
+
export interface WakeRouteEvidence {
|
|
21
|
+
sessionId: string;
|
|
22
|
+
address: Record<string, string>;
|
|
23
|
+
}
|
|
24
|
+
/** A provider's self-check: complete, callable endpoint evidence for one kind, or nothing. */
|
|
25
|
+
export type WakeRouteProbe = (env: NodeJS.ProcessEnv) => WakeRouteEvidence | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* One probe per kind. A probe publishes only when its provider's complete
|
|
28
|
+
* endpoint evidence is present; session identity alone never publishes. The
|
|
29
|
+
* four native transports have no endpoint lifecycle in this delivery, so
|
|
30
|
+
* their probes publish nothing until those transports land.
|
|
31
|
+
*/
|
|
32
|
+
export declare const WAKE_ROUTE_PROBES: Readonly<Record<WakeRouteKind, WakeRouteProbe>>;
|
|
33
|
+
/** The kind-neutral publication loop; probes supply complete evidence per kind. */
|
|
34
|
+
export declare function publishWakeRoutesFrom(ownerId: string, probes: Readonly<Record<WakeRouteKind, WakeRouteProbe>>, opts?: {
|
|
35
|
+
at?: number;
|
|
36
|
+
env?: NodeJS.ProcessEnv;
|
|
37
|
+
}): void;
|
|
38
|
+
/** Publication boundary: every route written is complete provider evidence. */
|
|
39
|
+
export declare function publishWakeRoutes(ownerId: string, opts?: {
|
|
40
|
+
at?: number;
|
|
41
|
+
env?: NodeJS.ProcessEnv;
|
|
42
|
+
}): void;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type ActivityId, type Reach } from './square-core.js';
|
|
2
|
+
import { type StoredAct, type SquareState, type HoldState, type ReadCursor, type WatchLease } from './model.js';
|
|
3
|
+
export declare const WATCH_HEARTBEAT_MS: number;
|
|
4
|
+
export declare const SLEEP_MS: number;
|
|
5
|
+
export declare const STALE_MS: number;
|
|
6
|
+
export declare const WATCH_STALE_MS: number;
|
|
7
|
+
export declare const LOCK_RETRY_MS: number;
|
|
8
|
+
export declare const LOCK_STALE_MS: number;
|
|
9
|
+
export declare const THROTTLE_WINDOW_MS: number;
|
|
10
|
+
export declare const UNREAD_BLOCK_GRACE_MS: number;
|
|
11
|
+
export declare function nowMs(): number;
|
|
12
|
+
/** Pure directed-activity filter. Bell matches every viewer; mentions match by audience. */
|
|
13
|
+
export declare function matchesMentionTarget(act: {
|
|
14
|
+
body: string;
|
|
15
|
+
reach?: Reach;
|
|
16
|
+
}, mention: string | true): boolean;
|
|
17
|
+
export declare function foldedState(squareState: SquareState): import("./square-core.js").FoldedSquareState;
|
|
18
|
+
export declare function rosterNames(squareState: SquareState): string[];
|
|
19
|
+
export declare function inSquareCount(squareState: SquareState): number;
|
|
20
|
+
export declare function resolveRosterName(squareState: SquareState, name: string): string | undefined;
|
|
21
|
+
export declare function hasQuorum(squareState: SquareState, name: string, outs: Set<string>): boolean;
|
|
22
|
+
export declare function countSays(acts: StoredAct[], name: string): number;
|
|
23
|
+
export declare function sayNumberFor(acts: StoredAct[], target: StoredAct): number;
|
|
24
|
+
/** Shared terminal condition for a participant's watch lifecycle. */
|
|
25
|
+
export declare function watchTerminalStatus(squareState: SquareState, name: string): 'capped' | 'quorum' | undefined;
|
|
26
|
+
export declare function doneNames(acts: StoredAct[]): Set<string>;
|
|
27
|
+
export declare function joinedNames(acts: StoredAct[]): Set<string>;
|
|
28
|
+
export declare function isCurrentlyJoined(acts: StoredAct[], name: string): boolean;
|
|
29
|
+
/** Stable index of the recipient's most recent join act, if any. */
|
|
30
|
+
export declare function lastJoinIndex(acts: StoredAct[], name: string): number | undefined;
|
|
31
|
+
export declare function actStableIndex(act: StoredAct): number;
|
|
32
|
+
export declare function actId(actOrIndex: StoredAct | number): ActivityId;
|
|
33
|
+
export declare function getReadState(squareState: SquareState, name: string): ReadCursor | undefined;
|
|
34
|
+
export declare function readCursor(squareState: SquareState, name: string): number;
|
|
35
|
+
export declare function currentHold(acts: StoredAct[]): HoldState;
|
|
36
|
+
export declare function publicActs(acts: StoredAct[]): Array<Extract<StoredAct, {
|
|
37
|
+
kind: 'say' | 'done';
|
|
38
|
+
}>>;
|
|
39
|
+
export declare function advanceCursor(squareState: SquareState, name: string, index: number, updatedAt?: number): boolean;
|
|
40
|
+
export declare function touchPresenceCursor(squareState: SquareState, name: string, at: number, consumedThroughIndex?: number): boolean;
|
|
41
|
+
export declare function latestActIndex(acts: StoredAct[]): number;
|
|
42
|
+
export declare function freshWatchLease(squareState: SquareState, name: string, at?: number): WatchLease | undefined;
|
|
43
|
+
export declare function watchLease(squareState: SquareState, name: string): WatchLease | undefined;
|
|
44
|
+
export declare function writeWatchLease(squareState: SquareState, name: string, lease: WatchLease): void;
|
|
45
|
+
export declare function removeWatchLease(squareState: SquareState, name: string, leaseId: string): boolean;
|
package/dist/runtime.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { fold } from './square-core.js';
|
|
1
|
+
import { audienceIncludes, audienceOf, fold, formatActivityId } from './square-core.js';
|
|
2
2
|
import { SquareError, findParticipantName, nameKey, sameName, } from './model.js';
|
|
3
3
|
function parseIntegerEnvValue(name, raw, fallback) {
|
|
4
4
|
if (raw === undefined)
|
|
@@ -51,42 +51,27 @@ export function nowMs() {
|
|
|
51
51
|
}
|
|
52
52
|
return value;
|
|
53
53
|
}
|
|
54
|
-
|
|
55
|
-
const matches = [];
|
|
56
|
-
const re = /@([\p{L}\p{N}_-]+)/gu;
|
|
57
|
-
let match;
|
|
58
|
-
while ((match = re.exec(body)) !== null)
|
|
59
|
-
matches.push(match[1]);
|
|
60
|
-
return matches;
|
|
61
|
-
}
|
|
62
|
-
/** Pure directed-activity filter. Broadcast bodies (no @) match any named viewer. */
|
|
54
|
+
/** Pure directed-activity filter. Bell matches every viewer; mentions match by audience. */
|
|
63
55
|
export function matchesMentionTarget(act, mention) {
|
|
64
|
-
|
|
65
|
-
return true;
|
|
66
|
-
if (act.reach !== undefined) {
|
|
67
|
-
return mention === true || sameName(act.reach.beside, mention);
|
|
68
|
-
}
|
|
69
|
-
const mentions = extractMentions(act.body);
|
|
56
|
+
const audience = audienceOf(act);
|
|
70
57
|
if (mention === true)
|
|
71
|
-
return
|
|
72
|
-
|
|
73
|
-
return true;
|
|
74
|
-
return mentions.some((name) => sameName(name, mention));
|
|
58
|
+
return audience.kind === 'bell' || audience.names.length > 0;
|
|
59
|
+
return audienceIncludes(audience, mention);
|
|
75
60
|
}
|
|
76
|
-
export function foldedState(
|
|
77
|
-
return fold(
|
|
61
|
+
export function foldedState(squareState) {
|
|
62
|
+
return fold(squareState.acts);
|
|
78
63
|
}
|
|
79
|
-
export function rosterNames(
|
|
80
|
-
return foldedState(
|
|
64
|
+
export function rosterNames(squareState) {
|
|
65
|
+
return foldedState(squareState).participants.map((participant) => participant.name);
|
|
81
66
|
}
|
|
82
|
-
export function inSquareCount(
|
|
83
|
-
return foldedState(
|
|
67
|
+
export function inSquareCount(squareState) {
|
|
68
|
+
return foldedState(squareState).joined.length;
|
|
84
69
|
}
|
|
85
|
-
export function resolveRosterName(
|
|
86
|
-
return findParticipantName(rosterNames(
|
|
70
|
+
export function resolveRosterName(squareState, name) {
|
|
71
|
+
return findParticipantName(rosterNames(squareState), name);
|
|
87
72
|
}
|
|
88
|
-
export function hasQuorum(
|
|
89
|
-
const peers = rosterNames(
|
|
73
|
+
export function hasQuorum(squareState, name, outs) {
|
|
74
|
+
const peers = rosterNames(squareState).filter((participant) => !sameName(participant, name));
|
|
90
75
|
return peers.length > 0 && peers.every((peer) => outs.has(nameKey(peer)));
|
|
91
76
|
}
|
|
92
77
|
export function countSays(acts, name) {
|
|
@@ -106,6 +91,14 @@ export function sayNumberFor(acts, target) {
|
|
|
106
91
|
}
|
|
107
92
|
throw new Error('target say act is not present in act history');
|
|
108
93
|
}
|
|
94
|
+
/** Shared terminal condition for a participant's watch lifecycle. */
|
|
95
|
+
export function watchTerminalStatus(squareState, name) {
|
|
96
|
+
if (squareState.hardCap !== null && countSays(squareState.acts, name) >= squareState.hardCap)
|
|
97
|
+
return 'capped';
|
|
98
|
+
const done = doneNames(squareState.acts);
|
|
99
|
+
done.delete(nameKey(name));
|
|
100
|
+
return hasQuorum(squareState, name, done) ? 'quorum' : undefined;
|
|
101
|
+
}
|
|
109
102
|
export function doneNames(acts) {
|
|
110
103
|
return new Set(fold(acts).done.map((participant) => nameKey(participant)));
|
|
111
104
|
}
|
|
@@ -131,13 +124,13 @@ export function actStableIndex(act) {
|
|
|
131
124
|
}
|
|
132
125
|
export function actId(actOrIndex) {
|
|
133
126
|
const index = typeof actOrIndex === 'number' ? actOrIndex : actStableIndex(actOrIndex);
|
|
134
|
-
return
|
|
127
|
+
return formatActivityId(index);
|
|
135
128
|
}
|
|
136
|
-
export function getReadState(
|
|
137
|
-
return
|
|
129
|
+
export function getReadState(squareState, name) {
|
|
130
|
+
return squareState.runtime.cursors[name] ?? Object.entries(squareState.runtime.cursors).find(([participant]) => sameName(participant, name))?.[1];
|
|
138
131
|
}
|
|
139
|
-
export function readCursor(
|
|
140
|
-
return getReadState(
|
|
132
|
+
export function readCursor(squareState, name) {
|
|
133
|
+
return getReadState(squareState, name)?.consumedThroughIndex ?? -1;
|
|
141
134
|
}
|
|
142
135
|
export function currentHold(acts) {
|
|
143
136
|
const hold = fold(acts).hold;
|
|
@@ -146,48 +139,48 @@ export function currentHold(acts) {
|
|
|
146
139
|
export function publicActs(acts) {
|
|
147
140
|
return acts.filter((act) => act.kind === 'say' || act.kind === 'done');
|
|
148
141
|
}
|
|
149
|
-
function canonicalRuntimeName(
|
|
150
|
-
return resolveRosterName(
|
|
142
|
+
function canonicalRuntimeName(squareState, name) {
|
|
143
|
+
return resolveRosterName(squareState, name) ?? name;
|
|
151
144
|
}
|
|
152
|
-
export function advanceCursor(
|
|
153
|
-
return touchPresenceCursor(
|
|
145
|
+
export function advanceCursor(squareState, name, index, updatedAt = Date.now()) {
|
|
146
|
+
return touchPresenceCursor(squareState, name, updatedAt, index);
|
|
154
147
|
}
|
|
155
|
-
export function touchPresenceCursor(
|
|
148
|
+
export function touchPresenceCursor(squareState, name, at, consumedThroughIndex) {
|
|
156
149
|
if (!Number.isFinite(at))
|
|
157
150
|
return false;
|
|
158
151
|
if (consumedThroughIndex !== undefined && (!Number.isInteger(consumedThroughIndex) || consumedThroughIndex < 0))
|
|
159
152
|
return false;
|
|
160
|
-
const key = canonicalRuntimeName(
|
|
161
|
-
const current = getReadState(
|
|
153
|
+
const key = canonicalRuntimeName(squareState, name);
|
|
154
|
+
const current = getReadState(squareState, key);
|
|
162
155
|
const nextIndex = consumedThroughIndex === undefined
|
|
163
|
-
? (current?.consumedThroughIndex ?? readCursor(
|
|
156
|
+
? (current?.consumedThroughIndex ?? readCursor(squareState, key))
|
|
164
157
|
: Math.max(current?.consumedThroughIndex ?? -1, consumedThroughIndex);
|
|
165
158
|
const updatedAt = current === undefined ? at : Math.max(current.updatedAt, at);
|
|
166
159
|
if (current?.consumedThroughIndex === nextIndex && current.updatedAt === updatedAt)
|
|
167
160
|
return false;
|
|
168
|
-
|
|
161
|
+
squareState.runtime.cursors[key] = { consumedThroughIndex: nextIndex, updatedAt };
|
|
169
162
|
return true;
|
|
170
163
|
}
|
|
171
164
|
export function latestActIndex(acts) {
|
|
172
165
|
return acts.reduce((max, act) => Math.max(max, act.index), -1);
|
|
173
166
|
}
|
|
174
|
-
export function freshWatchLease(
|
|
175
|
-
const key = canonicalRuntimeName(
|
|
176
|
-
const lease = watchLease(
|
|
167
|
+
export function freshWatchLease(squareState, name, at = Date.now()) {
|
|
168
|
+
const key = canonicalRuntimeName(squareState, name);
|
|
169
|
+
const lease = watchLease(squareState, key);
|
|
177
170
|
if (lease === undefined || lease.expiresAt <= at || at - lease.heartbeatAt > WATCH_STALE_MS)
|
|
178
171
|
return undefined;
|
|
179
172
|
return lease;
|
|
180
173
|
}
|
|
181
|
-
export function watchLease(
|
|
182
|
-
return
|
|
174
|
+
export function watchLease(squareState, name) {
|
|
175
|
+
return squareState.runtime.leases[canonicalRuntimeName(squareState, name)];
|
|
183
176
|
}
|
|
184
|
-
export function writeWatchLease(
|
|
185
|
-
|
|
177
|
+
export function writeWatchLease(squareState, name, lease) {
|
|
178
|
+
squareState.runtime.leases[canonicalRuntimeName(squareState, name)] = lease;
|
|
186
179
|
}
|
|
187
|
-
export function removeWatchLease(
|
|
188
|
-
const key = canonicalRuntimeName(
|
|
189
|
-
if (
|
|
180
|
+
export function removeWatchLease(squareState, name, leaseId) {
|
|
181
|
+
const key = canonicalRuntimeName(squareState, name);
|
|
182
|
+
if (squareState.runtime.leases[key]?.leaseId !== leaseId)
|
|
190
183
|
return false;
|
|
191
|
-
delete
|
|
184
|
+
delete squareState.runtime.leases[key];
|
|
192
185
|
return true;
|
|
193
186
|
}
|