@astrosheep/square 0.3.12 → 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 +111 -90
- package/dist/artifact.d.ts +20 -0
- package/dist/artifact.js +10 -29
- 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 +5 -4
- 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 +25 -30
- package/dist/cli/harness-command.d.ts +28 -0
- package/dist/cli/harness-command.js +29 -14
- package/dist/cli/maintenance-commands.d.ts +7 -0
- package/dist/cli/maintenance-commands.js +3 -3
- package/dist/cli/meta-commands.d.ts +5 -0
- package/dist/cli/observation-commands.d.ts +20 -0
- package/dist/cli/observation-commands.js +155 -128
- package/dist/cli/program.d.ts +2 -0
- package/dist/cli/program.js +2 -12
- package/dist/cli/registry.d.ts +9 -0
- package/dist/cli/registry.js +1 -2
- package/dist/cli/square-commands.d.ts +34 -0
- package/dist/cli/square-commands.js +81 -74
- 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 +83 -77
- package/dist/delivery-health.d.ts +21 -0
- package/dist/delivery-health.js +40 -34
- package/dist/delivery.d.ts +81 -0
- package/dist/delivery.js +60 -22
- 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 +6 -5
- 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 -69
- package/dist/landing.d.ts +10 -0
- package/dist/landing.js +95 -0
- package/dist/list.d.ts +1 -0
- package/dist/list.js +40 -18
- package/dist/model.d.ts +147 -0
- package/dist/model.js +12 -2
- package/dist/notifications.d.ts +30 -0
- package/dist/notifications.js +75 -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 +33 -29
- 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 +42 -34
- package/dist/search.d.ts +11 -0
- package/dist/square-core.d.ts +111 -0
- package/dist/square-core.js +7 -0
- 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-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/package.json +5 -2
- package/skills/brainstorm/SKILL.md +26 -26
- package/skills/square/.claude-plugin/plugin.json +1 -1
- package/skills/square/SKILL.md +47 -29
- package/skills/square/hooks/hooks.json +9 -0
- package/skills/square-feedback/SKILL.md +2 -2
- package/dist/compact.js +0 -26
- package/dist/square-application.js +0 -193
package/dist/inbox.js
CHANGED
|
@@ -1,34 +1,30 @@
|
|
|
1
|
-
import { loadSquare } from './artifact.js';
|
|
2
|
-
import { deriveDeliveryModel } from './delivery.js';
|
|
3
1
|
import { lookupSessionBindings } from './registry.js';
|
|
4
|
-
import {
|
|
5
|
-
|
|
2
|
+
import { openSquare } from './square-file-adapter.js';
|
|
3
|
+
import { closeOpenSquare } from './open-square.js';
|
|
4
|
+
import { inboxProjection } from './views.js';
|
|
5
|
+
export async function sessionInbox(sessionId) {
|
|
6
6
|
const inbox = [];
|
|
7
7
|
for (const binding of lookupSessionBindings(sessionId)) {
|
|
8
|
+
let square;
|
|
8
9
|
try {
|
|
9
|
-
|
|
10
|
-
const
|
|
11
|
-
if (!
|
|
10
|
+
square = await openSquare(binding.squarePath);
|
|
11
|
+
const projection = await inboxProjection(square, binding.name, binding.ownerId);
|
|
12
|
+
if (!projection.joined)
|
|
12
13
|
continue;
|
|
13
|
-
const notifications = deriveDeliveryModel(doc).pendingFor(name).map(({ item, route }) => ({
|
|
14
|
-
actIndex: item.index,
|
|
15
|
-
actor: item.actor,
|
|
16
|
-
at: item.at,
|
|
17
|
-
route,
|
|
18
|
-
body: item.body,
|
|
19
|
-
}));
|
|
20
|
-
const lease = freshWatchLease(doc, name);
|
|
21
|
-
const catchLease = lease?.ownerId === binding.ownerId ? lease : undefined;
|
|
22
14
|
inbox.push({
|
|
23
|
-
name,
|
|
15
|
+
name: projection.name,
|
|
24
16
|
squarePath: binding.squarePath,
|
|
25
|
-
notifications,
|
|
26
|
-
...(catchLease !== undefined ? { catchLease } : {}),
|
|
17
|
+
notifications: [...projection.notifications],
|
|
18
|
+
...(projection.catchLease !== undefined ? { catchLease: projection.catchLease } : {}),
|
|
27
19
|
});
|
|
28
20
|
}
|
|
29
21
|
catch {
|
|
30
22
|
// A stale discovery-cache row only disables delivery for that membership.
|
|
31
23
|
}
|
|
24
|
+
finally {
|
|
25
|
+
if (square !== undefined)
|
|
26
|
+
await closeOpenSquare(square);
|
|
27
|
+
}
|
|
32
28
|
}
|
|
33
29
|
return inbox;
|
|
34
30
|
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type SquareRoute } from './delivery.js';
|
|
2
|
+
import type { ExpressResult } from './square-facade.js';
|
|
3
|
+
export { Square } from './square-wiring.js';
|
|
4
|
+
export { SquareError } from './model.js';
|
|
5
|
+
export { captureRoute } from './delivery.js';
|
|
6
|
+
export type { SquareRoute } from './delivery.js';
|
|
7
|
+
export type { ActivityId } from './square-core.js';
|
|
8
|
+
export type { Activity, CatchOptions, CatchResult, ExpressOptions, ExpressResult, HistoryQuery, OpenOptions, Participant, ParticipantStatus, PerceivedActivity, SquareAtInput, SquareBuildInput, SquareSnapshot, SquareSource, WakeNotifier, } from './square-facade.js';
|
|
9
|
+
export interface RouteExpressOptions {
|
|
10
|
+
readonly as: string;
|
|
11
|
+
readonly body: string;
|
|
12
|
+
}
|
|
13
|
+
export declare function express(route: SquareRoute, options: RouteExpressOptions): Promise<ExpressResult>;
|
package/dist/index.js
CHANGED
|
@@ -1,71 +1,33 @@
|
|
|
1
|
+
import { extractMentions } from './square-core.js';
|
|
2
|
+
import { parseSquareRoute } from './delivery.js';
|
|
3
|
+
import { sameName, SquareError } from './model.js';
|
|
4
|
+
import { Square } from './square-wiring.js';
|
|
5
|
+
export { Square } from './square-wiring.js';
|
|
1
6
|
export { SquareError } from './model.js';
|
|
2
|
-
export {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
export function hasDeliveredNotification(squarePath, name, ref) {
|
|
29
|
-
return hasDeliveredNotificationImpl(squarePath, name, actRefIndex(ref));
|
|
30
|
-
}
|
|
31
|
-
export async function waitForDeliveredNotification(squarePath, name, ref, opts = {}) {
|
|
32
|
-
return waitForDeliveredNotificationImpl(squarePath, name, actRefIndex(ref), opts);
|
|
33
|
-
}
|
|
34
|
-
export function getParticipantPresence(squarePath, name, now = Date.now()) {
|
|
35
|
-
const doc = loadSquare(squarePath);
|
|
36
|
-
const known = resolveKnownName(doc, name);
|
|
37
|
-
const lease = freshWatchLease(doc, known, now);
|
|
38
|
-
return lease === undefined ? { watching: false } : { watching: true, lease };
|
|
39
|
-
}
|
|
40
|
-
export function isWatching(squarePath, name, now = Date.now()) {
|
|
41
|
-
return getParticipantPresence(squarePath, name, now).watching;
|
|
42
|
-
}
|
|
43
|
-
/** Typed participant intents share the same commit/effect pipeline as the CLI. */
|
|
44
|
-
export async function join(squarePath, name) {
|
|
45
|
-
await execute(squarePath, { type: 'join', name, now: Date.now() });
|
|
46
|
-
}
|
|
47
|
-
export async function done(squarePath, name, body = '') {
|
|
48
|
-
await execute(squarePath, { type: 'done', name, body, now: Date.now() });
|
|
49
|
-
}
|
|
50
|
-
export async function hold(squarePath, actor, body = '') {
|
|
51
|
-
await execute(squarePath, { type: 'hold', actor, body, now: Date.now() });
|
|
52
|
-
}
|
|
53
|
-
export async function resume(squarePath, actor) {
|
|
54
|
-
await execute(squarePath, { type: 'resume', actor, now: Date.now() });
|
|
55
|
-
}
|
|
56
|
-
export async function express(squarePath, name, body, opts = {}) {
|
|
57
|
-
await sweepPendingNotifications(squarePath);
|
|
58
|
-
const committed = await execute(squarePath, {
|
|
59
|
-
type: 'say',
|
|
60
|
-
name,
|
|
61
|
-
body,
|
|
62
|
-
force: opts.force ?? false,
|
|
63
|
-
now: Date.now(),
|
|
64
|
-
...(opts.reply === undefined ? {} : { reply: actRefIndex(opts.reply) }),
|
|
65
|
-
});
|
|
66
|
-
const sayAct = committed.acts.find((act) => act.kind === 'say');
|
|
67
|
-
if (sayAct !== undefined)
|
|
68
|
-
await dispatchActNotifications(squarePath, sayAct);
|
|
69
|
-
if (committed.result.type !== 'sent')
|
|
70
|
-
throw new Error(`Activity rejected: ${committed.result.type}`);
|
|
7
|
+
export { captureRoute } from './delivery.js';
|
|
8
|
+
function directedBody(body, recipient) {
|
|
9
|
+
if (body.trim() === '')
|
|
10
|
+
throw new SquareError('invalid_args', 'express body cannot be empty');
|
|
11
|
+
if (extractMentions(body).some((mention) => sameName(mention, recipient)))
|
|
12
|
+
return body;
|
|
13
|
+
return `${body}${/\s$/u.test(body) ? '' : ' '}@${recipient}`;
|
|
14
|
+
}
|
|
15
|
+
export async function express(route, options) {
|
|
16
|
+
const parsed = parseSquareRoute(route);
|
|
17
|
+
if (options === null || typeof options !== 'object' ||
|
|
18
|
+
typeof options.as !== 'string' || options.as.trim() === '' ||
|
|
19
|
+
typeof options.body !== 'string') {
|
|
20
|
+
throw new SquareError('invalid_args', 'Route express requires { as, body }');
|
|
21
|
+
}
|
|
22
|
+
const square = await Square.at({ path: parsed.squarePath });
|
|
23
|
+
try {
|
|
24
|
+
const recipient = (await square.participants()).find((participant) => participant.state === 'joined' && sameName(participant.name, parsed.name));
|
|
25
|
+
if (recipient === undefined)
|
|
26
|
+
throw new SquareError('invalid_args', `Unknown participant "${parsed.name}"`);
|
|
27
|
+
const sender = await square.join(options.as);
|
|
28
|
+
return sender.express(directedBody(options.body, recipient.name));
|
|
29
|
+
}
|
|
30
|
+
finally {
|
|
31
|
+
await square.close();
|
|
32
|
+
}
|
|
71
33
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { OpenSquare } from './open-square.js';
|
|
2
|
+
import type { Activity, ExpressOptions, ExpressResult } from './square-facade.js';
|
|
3
|
+
export declare function join(square: OpenSquare, name: string): Promise<{
|
|
4
|
+
readonly name: string;
|
|
5
|
+
readonly activity: Activity | null;
|
|
6
|
+
}>;
|
|
7
|
+
export declare function express(square: OpenSquare, name: string, body: string, options?: ExpressOptions): Promise<ExpressResult>;
|
|
8
|
+
export declare function done(square: OpenSquare, name: string, body?: string): Promise<ExpressResult>;
|
|
9
|
+
export declare function hold(square: OpenSquare, name: string, reason?: string): Promise<ExpressResult>;
|
|
10
|
+
export declare function resume(square: OpenSquare, name: string): Promise<ExpressResult>;
|
package/dist/landing.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { extractMentions, formatActivityId, parseActivityId } from './square-core.js';
|
|
2
|
+
import { coreDone, coreHold, coreResume, decideAct, decideJoin } from './decisions.js';
|
|
3
|
+
import { deriveDeliveryModel } from './delivery.js';
|
|
4
|
+
import { SquareError } from './model.js';
|
|
5
|
+
import { participantIdentity } from './participant-identity.js';
|
|
6
|
+
import { touchPresenceCursor } from './runtime.js';
|
|
7
|
+
function storeActs(state, acts) {
|
|
8
|
+
const stored = [];
|
|
9
|
+
for (const act of acts) {
|
|
10
|
+
const item = { ...act, index: state.runtime.nextActIndex };
|
|
11
|
+
state.runtime.nextActIndex += 1;
|
|
12
|
+
state.acts.push(item);
|
|
13
|
+
if (item.actor !== undefined)
|
|
14
|
+
touchPresenceCursor(state, item.actor, item.at, item.index);
|
|
15
|
+
stored.push(item);
|
|
16
|
+
}
|
|
17
|
+
return stored;
|
|
18
|
+
}
|
|
19
|
+
function committedActivity(stored, verb) {
|
|
20
|
+
const activity = stored[0];
|
|
21
|
+
if (activity === undefined)
|
|
22
|
+
throw new Error(`${verb} activity did not commit`);
|
|
23
|
+
return activity;
|
|
24
|
+
}
|
|
25
|
+
function exposeActivity(stored) {
|
|
26
|
+
if (stored.kind === 'read' || stored.actor === undefined)
|
|
27
|
+
throw new Error(`Cannot expose stored activity ${formatActivityId(stored.index)}`);
|
|
28
|
+
return {
|
|
29
|
+
id: formatActivityId(stored.index), at: stored.at, kind: stored.kind, actor: stored.actor,
|
|
30
|
+
...('body' in stored && stored.body !== undefined ? { body: stored.body } : {}),
|
|
31
|
+
mentions: stored.kind === 'say' ? extractMentions(stored.body) : [],
|
|
32
|
+
...(stored.kind === 'say' && stored.reply !== undefined ? { reply: formatActivityId(stored.reply) } : {}),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function parseRequiredActivityId(id) {
|
|
36
|
+
const index = parseActivityId(id);
|
|
37
|
+
if (index === undefined)
|
|
38
|
+
throw new SquareError('invalid_args', `Invalid activity id: ${id}`);
|
|
39
|
+
return index;
|
|
40
|
+
}
|
|
41
|
+
async function wakeAfterCommit(square, recipients, activity) {
|
|
42
|
+
if (recipients.length === 0)
|
|
43
|
+
return;
|
|
44
|
+
try {
|
|
45
|
+
square.notifier?.wake(recipients, activity);
|
|
46
|
+
}
|
|
47
|
+
catch { /* post-commit effects cannot undo activities */ }
|
|
48
|
+
}
|
|
49
|
+
export async function join(square, name) {
|
|
50
|
+
const now = square.clock();
|
|
51
|
+
const committed = await square.cell.transact((state) => {
|
|
52
|
+
const decision = decideJoin(state, name, now);
|
|
53
|
+
if (decision.joinAct === undefined)
|
|
54
|
+
return { result: { name: decision.joinedName, stored: null } };
|
|
55
|
+
return { state, result: { name: decision.joinedName, stored: committedActivity(storeActs(state, [decision.joinAct]), 'join') } };
|
|
56
|
+
});
|
|
57
|
+
return { name: committed.name, activity: committed.stored === null ? null : exposeActivity(committed.stored) };
|
|
58
|
+
}
|
|
59
|
+
export async function express(square, name, body, options = {}) {
|
|
60
|
+
const now = square.clock();
|
|
61
|
+
const reply = options.reply === undefined ? undefined : parseRequiredActivityId(options.reply);
|
|
62
|
+
const committed = await square.cell.transact((state) => {
|
|
63
|
+
const decision = decideAct(state, { name, body, force: options.force ?? false, now, ...(options.reach === undefined ? {} : { reach: options.reach }), ...(reply === undefined ? {} : { reply }) });
|
|
64
|
+
if (decision.type === 'blocked') {
|
|
65
|
+
const pending = decision.activitySummaries.reduce((count, summary) => count + summary.count, 0) + decision.unreadRoomChanges.length;
|
|
66
|
+
throw new SquareError('behind', `${participantIdentity(name)} has pending activity`, { pending });
|
|
67
|
+
}
|
|
68
|
+
if (decision.type === 'held') {
|
|
69
|
+
const holder = state.acts.filter((activity) => activity.kind === 'hold').at(-1)?.actor;
|
|
70
|
+
throw new SquareError('held', 'The square is held', holder === undefined ? undefined : { holder });
|
|
71
|
+
}
|
|
72
|
+
if (decision.type === 'capped')
|
|
73
|
+
throw new SquareError('capped', `${participantIdentity(name)} reached the activity cap`);
|
|
74
|
+
if (decision.type === 'throttled')
|
|
75
|
+
throw new SquareError('throttled', `${name} is throttled`, { retryAfterMs: decision.delayMs });
|
|
76
|
+
if (decision.type === 'bell_quota')
|
|
77
|
+
throw new SquareError('bell_quota', `${participantIdentity(name)} cannot ring the bell yet`, { retryAfterMs: Math.max(1, decision.nextAt - now) });
|
|
78
|
+
const stored = committedActivity(storeActs(state, [decision.act]), 'express');
|
|
79
|
+
return { state, result: { stored, recipients: deriveDeliveryModel(state).plan(stored).map((notification) => notification.recipient) } };
|
|
80
|
+
});
|
|
81
|
+
const activity = exposeActivity(committed.stored);
|
|
82
|
+
await wakeAfterCommit(square, committed.recipients, activity);
|
|
83
|
+
return { activity };
|
|
84
|
+
}
|
|
85
|
+
async function landCore(square, verb, actor, body = '') {
|
|
86
|
+
const now = square.clock();
|
|
87
|
+
const stored = await square.cell.transact((state) => {
|
|
88
|
+
const act = verb === 'done' ? coreDone(state, actor, body, now) : verb === 'hold' ? coreHold(state, actor, body, now) : coreResume(state, actor, now);
|
|
89
|
+
return { state, result: committedActivity(storeActs(state, [act]), verb) };
|
|
90
|
+
});
|
|
91
|
+
return { activity: exposeActivity(stored) };
|
|
92
|
+
}
|
|
93
|
+
export function done(square, name, body = '') { return landCore(square, 'done', name, body); }
|
|
94
|
+
export function hold(square, name, reason = '') { return landCore(square, 'hold', name, reason); }
|
|
95
|
+
export function resume(square, name) { return landCore(square, 'resume', name); }
|
package/dist/list.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function cmdListSquares(args: string[], usage: () => void): Promise<void>;
|
package/dist/list.js
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { probeSquare } from './
|
|
4
|
-
import {
|
|
3
|
+
import { probeSquare } from './square-file-adapter.js';
|
|
4
|
+
import { closeOpenSquare } from './open-square.js';
|
|
5
|
+
import { listPresentation } from './views.js';
|
|
5
6
|
import { formatRelativeTime } from './time.js';
|
|
7
|
+
import { participantIdentity } from './presentation.js';
|
|
6
8
|
const DEFAULT_LIST_DEPTH = 4;
|
|
9
|
+
const CONTEXT_PREVIEW_LINES = 2;
|
|
10
|
+
const PARTICIPANT_PREVIEW_COUNT = 3;
|
|
7
11
|
const LIST_SKIP_DIRS = new Set(['.git', 'node_modules', 'dist']);
|
|
8
|
-
function
|
|
12
|
+
function contextLines(lines) {
|
|
13
|
+
return lines.map((line) => line.trim()).filter(Boolean);
|
|
14
|
+
}
|
|
15
|
+
async function readSquareListItem(filePath, root) {
|
|
9
16
|
let stat;
|
|
10
17
|
try {
|
|
11
18
|
stat = fs.statSync(filePath);
|
|
@@ -13,20 +20,22 @@ function readSquareListItem(filePath, root) {
|
|
|
13
20
|
catch {
|
|
14
21
|
return null;
|
|
15
22
|
}
|
|
16
|
-
const
|
|
17
|
-
if (
|
|
23
|
+
const square = probeSquare(filePath);
|
|
24
|
+
if (square === undefined)
|
|
18
25
|
return null;
|
|
26
|
+
const projection = await listPresentation(square).finally(() => closeOpenSquare(square));
|
|
19
27
|
const relative = path.relative(root, filePath) || path.basename(filePath);
|
|
20
28
|
return {
|
|
21
29
|
path: relative,
|
|
22
30
|
lastActiveAt: stat.mtimeMs,
|
|
23
|
-
|
|
24
|
-
|
|
31
|
+
context: contextLines([...projection.context]),
|
|
32
|
+
participants: [...projection.participants],
|
|
33
|
+
activities: projection.activities,
|
|
25
34
|
};
|
|
26
35
|
}
|
|
27
|
-
function collectSquareList(root, maxDepth) {
|
|
36
|
+
async function collectSquareList(root, maxDepth) {
|
|
28
37
|
const items = [];
|
|
29
|
-
function walk(dir, depth) {
|
|
38
|
+
async function walk(dir, depth) {
|
|
30
39
|
let entries;
|
|
31
40
|
try {
|
|
32
41
|
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
@@ -39,27 +48,40 @@ function collectSquareList(root, maxDepth) {
|
|
|
39
48
|
const fullPath = path.join(dir, entry.name);
|
|
40
49
|
if (entry.isDirectory()) {
|
|
41
50
|
if (depth < maxDepth && !LIST_SKIP_DIRS.has(entry.name))
|
|
42
|
-
walk(fullPath, depth + 1);
|
|
51
|
+
await walk(fullPath, depth + 1);
|
|
43
52
|
continue;
|
|
44
53
|
}
|
|
45
54
|
if (!entry.isFile())
|
|
46
55
|
continue;
|
|
47
|
-
const item = readSquareListItem(fullPath, root);
|
|
56
|
+
const item = await readSquareListItem(fullPath, root);
|
|
48
57
|
if (item)
|
|
49
58
|
items.push(item);
|
|
50
59
|
}
|
|
51
60
|
}
|
|
52
|
-
walk(root, 0);
|
|
61
|
+
await walk(root, 0);
|
|
53
62
|
return items.sort((a, b) => a.path.localeCompare(b.path));
|
|
54
63
|
}
|
|
55
64
|
function renderSquareList(items) {
|
|
56
65
|
if (items.length === 0)
|
|
57
66
|
return '(no squares found)\n';
|
|
58
67
|
const now = Date.now();
|
|
59
|
-
const lines = [
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
68
|
+
const lines = ['squares'];
|
|
69
|
+
for (const item of items) {
|
|
70
|
+
lines.push(`${item.activities > 0 ? '●' : '○'} ${item.path} · ${formatRelativeTime(item.lastActiveAt, now)} · ${item.participants.length} in square · ${item.activities} activities`);
|
|
71
|
+
const shownContext = item.context.slice(0, CONTEXT_PREVIEW_LINES);
|
|
72
|
+
if (shownContext.length === 0) {
|
|
73
|
+
lines.push(' context · (none)');
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
shownContext.forEach((line, index) => lines.push(` ${index === 0 ? 'context' : ' '} · ${line}`));
|
|
77
|
+
const hiddenContext = item.context.length - shownContext.length;
|
|
78
|
+
if (hiddenContext > 0)
|
|
79
|
+
lines.push(` · … ${hiddenContext} more ${hiddenContext === 1 ? 'line' : 'lines'}`);
|
|
80
|
+
}
|
|
81
|
+
const shownParticipants = item.participants.slice(0, PARTICIPANT_PREVIEW_COUNT);
|
|
82
|
+
const hiddenParticipants = item.participants.length - shownParticipants.length;
|
|
83
|
+
lines.push(` participants · ${shownParticipants.length === 0 ? 'nobody' : shownParticipants.map(participantIdentity).join(' · ')}${hiddenParticipants > 0 ? ` · … ${hiddenParticipants} more` : ''}`);
|
|
84
|
+
}
|
|
63
85
|
return lines.join('\n') + '\n';
|
|
64
86
|
}
|
|
65
87
|
function parseMaxDepth(args, usage) {
|
|
@@ -76,7 +98,7 @@ function parseMaxDepth(args, usage) {
|
|
|
76
98
|
}
|
|
77
99
|
return depth;
|
|
78
100
|
}
|
|
79
|
-
export function cmdListSquares(args, usage) {
|
|
101
|
+
export async function cmdListSquares(args, usage) {
|
|
80
102
|
const maxDepth = parseMaxDepth(args, usage);
|
|
81
|
-
process.stdout.write(renderSquareList(collectSquareList(process.cwd(), maxDepth)));
|
|
103
|
+
process.stdout.write(renderSquareList(await collectSquareList(process.cwd(), maxDepth)));
|
|
82
104
|
}
|
package/dist/model.d.ts
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import type { Act } from './square-core.js';
|
|
2
|
+
export { formatActivityId, parseActivityId } from './square-core.js';
|
|
3
|
+
export type { Act, ActivityId, Audience, Reach } from './square-core.js';
|
|
4
|
+
export declare const WAKE_ROUTE_KINDS: readonly ["opencode-server", "codex-app-server", "claude-native", "pi-extension", "paseo"];
|
|
5
|
+
export type WakeRouteKind = typeof WAKE_ROUTE_KINDS[number];
|
|
6
|
+
export declare function isWakeRouteKind(value: unknown): value is WakeRouteKind;
|
|
7
|
+
export interface WakeRoute {
|
|
8
|
+
ownerId: string;
|
|
9
|
+
sessionId: string;
|
|
10
|
+
kind: WakeRouteKind;
|
|
11
|
+
address: Record<string, string>;
|
|
12
|
+
updatedAt: number;
|
|
13
|
+
}
|
|
14
|
+
export type SquareErrorCode = 'invalid_name' | 'invalid_args' | 'unknown_participant' | 'not_joined' | 'already_joined' | 'already_done' | 'held' | 'capped' | 'throttled' | 'bell_quota' | 'behind' | 'io' | 'unavailable';
|
|
15
|
+
export type InternalSquareErrorCode = SquareErrorCode | 'not_found' | 'cap_reached' | 'conflict' | 'pending_peer';
|
|
16
|
+
export interface SquareErrorFacts {
|
|
17
|
+
pending?: number;
|
|
18
|
+
holder?: string;
|
|
19
|
+
retryAfterMs?: number;
|
|
20
|
+
}
|
|
21
|
+
declare class SquareErrorBase<Code extends string> extends Error {
|
|
22
|
+
code: Code;
|
|
23
|
+
facts?: SquareErrorFacts | undefined;
|
|
24
|
+
constructor(code: Code, message: string, facts?: SquareErrorFacts | undefined);
|
|
25
|
+
}
|
|
26
|
+
export declare class SquareError extends SquareErrorBase<SquareErrorCode> {
|
|
27
|
+
}
|
|
28
|
+
export declare class InternalSquareError extends SquareErrorBase<InternalSquareErrorCode> {
|
|
29
|
+
}
|
|
30
|
+
export declare function isSquareError(error: unknown): error is SquareError | InternalSquareError;
|
|
31
|
+
export type HardCap = number | null;
|
|
32
|
+
export interface BuildOptions {
|
|
33
|
+
force: boolean;
|
|
34
|
+
hardCap?: HardCap;
|
|
35
|
+
template?: string;
|
|
36
|
+
throttlePerMinute?: number;
|
|
37
|
+
}
|
|
38
|
+
export type StoredAct = Act & {
|
|
39
|
+
index: number;
|
|
40
|
+
at: number;
|
|
41
|
+
};
|
|
42
|
+
export type StoredActHead = StoredAct extends infer T ? T extends StoredAct ? Omit<T, 'body' | 'through' | 'index'> : never : never;
|
|
43
|
+
export interface ReadCursor {
|
|
44
|
+
consumedThroughIndex: number;
|
|
45
|
+
updatedAt: number;
|
|
46
|
+
}
|
|
47
|
+
export interface DeliveryReceipt {
|
|
48
|
+
/** Inject presentation lives only in the machine-local presented ledger. */
|
|
49
|
+
status: 'delivered';
|
|
50
|
+
at: number;
|
|
51
|
+
}
|
|
52
|
+
export interface WatchLeaseFilter {
|
|
53
|
+
participants?: string[];
|
|
54
|
+
mention?: string;
|
|
55
|
+
}
|
|
56
|
+
export interface WatchLease {
|
|
57
|
+
leaseId: string;
|
|
58
|
+
/** Machine-local owner that may claim automatic delivery for this lease. */
|
|
59
|
+
ownerId?: string;
|
|
60
|
+
heartbeatAt: number;
|
|
61
|
+
expiresAt: number;
|
|
62
|
+
filter?: WatchLeaseFilter;
|
|
63
|
+
}
|
|
64
|
+
export type DirectedNotificationRoute = 'mention' | 'bell';
|
|
65
|
+
export interface InboxNotification {
|
|
66
|
+
actIndex: number;
|
|
67
|
+
actor: string;
|
|
68
|
+
at: number;
|
|
69
|
+
route: DirectedNotificationRoute;
|
|
70
|
+
body: string;
|
|
71
|
+
}
|
|
72
|
+
export interface InboxMembership {
|
|
73
|
+
name: string;
|
|
74
|
+
squarePath: string;
|
|
75
|
+
notifications: InboxNotification[];
|
|
76
|
+
catchLease?: WatchLease;
|
|
77
|
+
}
|
|
78
|
+
export interface NotifyLease {
|
|
79
|
+
leaseId: string;
|
|
80
|
+
expiresAt: number;
|
|
81
|
+
phase: 'claimed' | 'dispatching';
|
|
82
|
+
attemptN?: number;
|
|
83
|
+
routeKind?: WakeRouteKind;
|
|
84
|
+
}
|
|
85
|
+
export interface SquareRuntimeState {
|
|
86
|
+
nextActIndex: number;
|
|
87
|
+
cursors: Record<string, ReadCursor>;
|
|
88
|
+
deliveryReceipts: Record<string, Record<string, DeliveryReceipt>>;
|
|
89
|
+
leases: Record<string, WatchLease>;
|
|
90
|
+
notifyLeases: Record<string, NotifyLease>;
|
|
91
|
+
}
|
|
92
|
+
export interface SquareState {
|
|
93
|
+
hardCap: HardCap;
|
|
94
|
+
throttlePerMinute?: number;
|
|
95
|
+
preamble: string[];
|
|
96
|
+
warmup: string[];
|
|
97
|
+
acts: StoredAct[];
|
|
98
|
+
runtime: SquareRuntimeState;
|
|
99
|
+
}
|
|
100
|
+
export interface ActivitiesOptions {
|
|
101
|
+
lastN?: number | null;
|
|
102
|
+
participants?: string[];
|
|
103
|
+
/** Exclusive upper bound on act.at (ms). */
|
|
104
|
+
before?: number;
|
|
105
|
+
/** Exclusive lower bound on act.at (ms). Alias of --since. */
|
|
106
|
+
after?: number;
|
|
107
|
+
/** Exclusive lower bound on stable act index (--after act/<index>). */
|
|
108
|
+
afterIndex?: number;
|
|
109
|
+
/** Center act indexes for context windows (--at). */
|
|
110
|
+
atIndexes?: number[];
|
|
111
|
+
beforeContext?: number;
|
|
112
|
+
afterContext?: number;
|
|
113
|
+
mention?: string;
|
|
114
|
+
/** Undelivered mention/bell items for viewer only (read-only; requires viewer). */
|
|
115
|
+
pending?: boolean;
|
|
116
|
+
viewer?: string;
|
|
117
|
+
full?: boolean;
|
|
118
|
+
grep?: string;
|
|
119
|
+
fixed?: string;
|
|
120
|
+
order?: 'asc' | 'desc';
|
|
121
|
+
format?: string[];
|
|
122
|
+
json?: boolean;
|
|
123
|
+
}
|
|
124
|
+
export interface WatchOptions {
|
|
125
|
+
participants?: string[];
|
|
126
|
+
mention?: string;
|
|
127
|
+
idleMs?: number;
|
|
128
|
+
replace?: boolean;
|
|
129
|
+
now?: boolean;
|
|
130
|
+
}
|
|
131
|
+
export type PublicAct = Extract<StoredAct, {
|
|
132
|
+
kind: 'say' | 'done';
|
|
133
|
+
}>;
|
|
134
|
+
export type RoomChangeAct = Extract<StoredAct, {
|
|
135
|
+
kind: 'join' | 'done' | 'hold' | 'resume';
|
|
136
|
+
}>;
|
|
137
|
+
export interface HoldState {
|
|
138
|
+
active: boolean;
|
|
139
|
+
at?: number;
|
|
140
|
+
reason?: string;
|
|
141
|
+
}
|
|
142
|
+
export declare function formatHardCap(hardCap: HardCap): string;
|
|
143
|
+
export declare function parseParticipantList(value: string): string[];
|
|
144
|
+
export declare function nameKey(name: string): string;
|
|
145
|
+
export declare function sameName(a: string, b: string): boolean;
|
|
146
|
+
export declare function findParticipantName(participants: string[], name: string): string | undefined;
|
|
147
|
+
export declare function validateName(name: string): void;
|
package/dist/model.js
CHANGED
|
@@ -1,16 +1,26 @@
|
|
|
1
1
|
// Shared model and constants for Square.
|
|
2
|
+
export { formatActivityId, parseActivityId } from './square-core.js';
|
|
2
3
|
export const WAKE_ROUTE_KINDS = ['opencode-server', 'codex-app-server', 'claude-native', 'pi-extension', 'paseo'];
|
|
3
4
|
export function isWakeRouteKind(value) {
|
|
4
5
|
return typeof value === 'string' && WAKE_ROUTE_KINDS.includes(value);
|
|
5
6
|
}
|
|
6
|
-
|
|
7
|
+
class SquareErrorBase extends Error {
|
|
7
8
|
code;
|
|
8
|
-
|
|
9
|
+
facts;
|
|
10
|
+
constructor(code, message, facts) {
|
|
9
11
|
super(message);
|
|
10
12
|
this.code = code;
|
|
13
|
+
this.facts = facts;
|
|
11
14
|
this.name = 'SquareError';
|
|
12
15
|
}
|
|
13
16
|
}
|
|
17
|
+
export class SquareError extends SquareErrorBase {
|
|
18
|
+
}
|
|
19
|
+
export class InternalSquareError extends SquareErrorBase {
|
|
20
|
+
}
|
|
21
|
+
export function isSquareError(error) {
|
|
22
|
+
return error instanceof SquareError || error instanceof InternalSquareError;
|
|
23
|
+
}
|
|
14
24
|
export function formatHardCap(hardCap) {
|
|
15
25
|
return hardCap === null ? '-1' : String(hardCap);
|
|
16
26
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { planActNotifications, type WakeAdapter } from './delivery.js';
|
|
2
|
+
import { matchesMentionTarget } from './runtime.js';
|
|
3
|
+
import { type ActivityId } from './square-core.js';
|
|
4
|
+
import type { WakeNotifier } from './square-facade.js';
|
|
5
|
+
export type { PlannedNotification } from './delivery.js';
|
|
6
|
+
export { planActNotifications, matchesMentionTarget };
|
|
7
|
+
export { notificationMessageId } from './delivery.js';
|
|
8
|
+
export declare function wakeGraceMs(env?: NodeJS.ProcessEnv): number;
|
|
9
|
+
export declare function hasDeliveredNotification(squarePath: string, name: string, ref: number | ActivityId): Promise<boolean>;
|
|
10
|
+
export declare function hasAttentionNotification(squarePath: string, name: string, ref: number | ActivityId, env?: NodeJS.ProcessEnv): Promise<boolean>;
|
|
11
|
+
export declare function waitForDeliveredNotification(squarePath: string, name: string, ref: number | ActivityId, opts?: {
|
|
12
|
+
timeoutMs?: number;
|
|
13
|
+
}): Promise<boolean>;
|
|
14
|
+
interface ProcessNotificationOptions {
|
|
15
|
+
adapters?: WakeAdapter[];
|
|
16
|
+
env?: NodeJS.ProcessEnv;
|
|
17
|
+
now?: () => number;
|
|
18
|
+
}
|
|
19
|
+
export declare function processActNotificationsOnce(squarePath: string, actIndex: number, opts?: ProcessNotificationOptions): Promise<void>;
|
|
20
|
+
interface WorkerLaunchOptions {
|
|
21
|
+
launchWorker?: (workerPath: string, args: string[]) => void;
|
|
22
|
+
env?: NodeJS.ProcessEnv;
|
|
23
|
+
}
|
|
24
|
+
export declare function wakeNotifierForSquare(squarePath: string, env?: NodeJS.ProcessEnv): WakeNotifier;
|
|
25
|
+
export interface SweepPendingNotificationsOptions extends WorkerLaunchOptions {
|
|
26
|
+
now?: number;
|
|
27
|
+
limit?: number;
|
|
28
|
+
}
|
|
29
|
+
/** Reconsider old pending attention at a bounded action boundary using the existing worker. */
|
|
30
|
+
export declare function sweepPendingNotifications(squarePath: string, opts?: SweepPendingNotificationsOptions): Promise<number[]>;
|