@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.
Files changed (121) hide show
  1. package/codex-plugin/.codex-plugin/plugin.json +1 -1
  2. package/codex-plugin/hooks/hooks.json +9 -0
  3. package/dist/activity-feed.d.ts +14 -0
  4. package/dist/activity-feed.js +5 -5
  5. package/dist/activity.d.ts +9 -0
  6. package/dist/activity.js +111 -90
  7. package/dist/artifact.d.ts +20 -0
  8. package/dist/artifact.js +10 -29
  9. package/dist/automatic-session.d.ts +5 -0
  10. package/dist/automatic-session.js +86 -0
  11. package/dist/boundary-presentation.d.ts +5 -0
  12. package/dist/boundary-presentation.js +5 -4
  13. package/dist/claude-hook.d.ts +9 -0
  14. package/dist/claude-hook.js +35 -3
  15. package/dist/cli/context.d.ts +35 -0
  16. package/dist/cli/context.js +25 -30
  17. package/dist/cli/harness-command.d.ts +28 -0
  18. package/dist/cli/harness-command.js +29 -14
  19. package/dist/cli/maintenance-commands.d.ts +7 -0
  20. package/dist/cli/maintenance-commands.js +3 -3
  21. package/dist/cli/meta-commands.d.ts +5 -0
  22. package/dist/cli/observation-commands.d.ts +20 -0
  23. package/dist/cli/observation-commands.js +155 -128
  24. package/dist/cli/program.d.ts +2 -0
  25. package/dist/cli/program.js +2 -12
  26. package/dist/cli/registry.d.ts +9 -0
  27. package/dist/cli/registry.js +1 -2
  28. package/dist/cli/square-commands.d.ts +34 -0
  29. package/dist/cli/square-commands.js +81 -74
  30. package/dist/cmd/notify-once.d.ts +2 -0
  31. package/dist/codex-hook.d.ts +10 -0
  32. package/dist/codex-hook.js +35 -3
  33. package/dist/decisions.d.ts +92 -0
  34. package/dist/decisions.js +83 -77
  35. package/dist/delivery-health.d.ts +21 -0
  36. package/dist/delivery-health.js +40 -34
  37. package/dist/delivery.d.ts +81 -0
  38. package/dist/delivery.js +60 -22
  39. package/dist/file-lock.d.ts +6 -0
  40. package/dist/harness-claude.d.ts +19 -0
  41. package/dist/harness-claude.js +8 -3
  42. package/dist/harness-codex.d.ts +29 -0
  43. package/dist/harness-codex.js +2 -2
  44. package/dist/harness-links.d.ts +17 -0
  45. package/dist/harness-links.js +2 -2
  46. package/dist/harness-pi.d.ts +11 -0
  47. package/dist/harness-pi.js +2 -2
  48. package/dist/harness-stage.d.ts +6 -0
  49. package/dist/harness.d.ts +20 -0
  50. package/dist/harness.js +5 -3
  51. package/dist/help.d.ts +6 -0
  52. package/dist/help.js +6 -5
  53. package/dist/identity.d.ts +13 -0
  54. package/dist/inbox.d.ts +2 -0
  55. package/dist/inbox.js +15 -19
  56. package/dist/index.d.ts +13 -0
  57. package/dist/index.js +31 -69
  58. package/dist/landing.d.ts +10 -0
  59. package/dist/landing.js +95 -0
  60. package/dist/list.d.ts +1 -0
  61. package/dist/list.js +40 -18
  62. package/dist/model.d.ts +147 -0
  63. package/dist/model.js +12 -2
  64. package/dist/notifications.d.ts +30 -0
  65. package/dist/notifications.js +75 -91
  66. package/dist/open-square.d.ts +11 -0
  67. package/dist/open-square.js +4 -0
  68. package/dist/participant-identity.d.ts +1 -0
  69. package/dist/participant-identity.js +3 -0
  70. package/dist/paseo-connection.d.ts +15 -0
  71. package/dist/paseo-delivery.d.ts +14 -0
  72. package/dist/paseo-state.d.ts +11 -0
  73. package/dist/paseo-timeline.d.ts +16 -0
  74. package/dist/presence.d.ts +3 -0
  75. package/dist/presence.js +42 -0
  76. package/dist/presentation.d.ts +91 -0
  77. package/dist/presentation.js +33 -29
  78. package/dist/presented.d.ts +11 -0
  79. package/dist/registry.d.ts +58 -0
  80. package/dist/registry.js +36 -3
  81. package/dist/routes.d.ts +42 -0
  82. package/dist/runtime.d.ts +45 -0
  83. package/dist/runtime.js +42 -34
  84. package/dist/search.d.ts +11 -0
  85. package/dist/square-core.d.ts +111 -0
  86. package/dist/square-core.js +7 -0
  87. package/dist/square-facade.d.ts +86 -0
  88. package/dist/square-facade.js +1 -0
  89. package/dist/square-file-adapter.d.ts +18 -0
  90. package/dist/square-file-adapter.js +70 -0
  91. package/dist/square-storage.d.ts +20 -0
  92. package/dist/square-storage.js +171 -0
  93. package/dist/square-wiring.d.ts +19 -0
  94. package/dist/square-wiring.js +70 -0
  95. package/dist/square.d.ts +2 -0
  96. package/dist/state-cell.d.ts +14 -0
  97. package/dist/state-cell.js +1 -0
  98. package/dist/stream.d.ts +3 -0
  99. package/dist/stream.js +19 -23
  100. package/dist/time.d.ts +6 -0
  101. package/dist/views.d.ts +95 -0
  102. package/dist/views.js +81 -0
  103. package/dist/wake-attempts.d.ts +39 -0
  104. package/dist/wake-evidence.d.ts +12 -0
  105. package/dist/wake-evidence.js +7 -10
  106. package/dist/wake-port.d.ts +20 -0
  107. package/dist/wake-sink.d.ts +12 -0
  108. package/dist/wakes.d.ts +40 -0
  109. package/dist/wakes.js +62 -0
  110. package/dist/watch.d.ts +2 -0
  111. package/dist/watch.js +89 -210
  112. package/extensions/square-opencode.js +27 -1
  113. package/extensions/square-pi.js +13 -0
  114. package/package.json +5 -2
  115. package/skills/brainstorm/SKILL.md +26 -26
  116. package/skills/square/.claude-plugin/plugin.json +1 -1
  117. package/skills/square/SKILL.md +47 -29
  118. package/skills/square/hooks/hooks.json +9 -0
  119. package/skills/square-feedback/SKILL.md +2 -2
  120. package/dist/compact.js +0 -26
  121. package/dist/square-application.js +0 -193
@@ -181,6 +181,8 @@ export function validate(state, act, options = {}) {
181
181
  case 'say': {
182
182
  if (current?.done)
183
183
  return { ok: false, reason: 'done' };
184
+ if (current?.joined !== true)
185
+ return { ok: false, reason: 'not_joined' };
184
186
  if (state.hold.active)
185
187
  return { ok: false, reason: 'held', hold: state.hold };
186
188
  const activityCount = current?.activityCount ?? 0;
@@ -209,6 +211,11 @@ export function validate(state, act, options = {}) {
209
211
  }
210
212
  case 'hold':
211
213
  case 'resume':
214
+ if (current?.done)
215
+ return { ok: false, reason: 'done' };
216
+ if (current?.joined !== true)
217
+ return { ok: false, reason: 'not_joined' };
218
+ return { ok: true };
212
219
  case 'read':
213
220
  return { ok: true };
214
221
  }
@@ -0,0 +1,86 @@
1
+ import type { ActivityId, Perception, Reach } from './square-core.js';
2
+ export interface Activity {
3
+ readonly id: ActivityId;
4
+ readonly at: number;
5
+ readonly kind: 'join' | 'say' | 'done' | 'hold' | 'resume';
6
+ readonly actor: string;
7
+ readonly body?: string;
8
+ readonly mentions: readonly string[];
9
+ readonly reply?: ActivityId;
10
+ }
11
+ export interface WakeNotifier {
12
+ wake(recipients: readonly string[], activity: Activity): void;
13
+ }
14
+ export interface PerceivedActivity extends Activity {
15
+ readonly perception: Perception;
16
+ }
17
+ export interface ExpressOptions {
18
+ readonly force?: boolean;
19
+ readonly reach?: Reach;
20
+ readonly reply?: ActivityId;
21
+ }
22
+ export interface ExpressResult {
23
+ readonly activity: Activity;
24
+ }
25
+ export interface CatchOptions {
26
+ readonly idle?: number;
27
+ readonly from?: readonly string[];
28
+ readonly mention?: boolean;
29
+ }
30
+ export interface CatchResult {
31
+ readonly activities: readonly PerceivedActivity[];
32
+ readonly consumedThrough: ActivityId | null;
33
+ readonly idleExpired: boolean;
34
+ }
35
+ export interface HistoryQuery {
36
+ readonly limit?: number;
37
+ readonly order?: 'asc' | 'desc';
38
+ readonly all?: boolean;
39
+ readonly full?: boolean;
40
+ readonly grep?: string;
41
+ readonly from?: readonly string[];
42
+ readonly mention?: boolean;
43
+ }
44
+ export interface ParticipantStatus {
45
+ readonly name: string;
46
+ readonly state: 'joined' | 'done';
47
+ readonly consumedThrough: ActivityId | null;
48
+ readonly watching: boolean;
49
+ }
50
+ export interface SquareSnapshot {
51
+ readonly context: string;
52
+ readonly actCount: number;
53
+ readonly hardCap: number | null;
54
+ readonly throttlePerMinute?: number;
55
+ readonly held: {
56
+ readonly by: string;
57
+ readonly reason?: string;
58
+ } | null;
59
+ readonly participants: readonly ParticipantStatus[];
60
+ delivered(name: string, id: ActivityId): boolean;
61
+ }
62
+ export type SquareSource = {
63
+ path: string;
64
+ };
65
+ export interface OpenOptions {
66
+ clock?: () => number;
67
+ notifier?: WakeNotifier;
68
+ }
69
+ export interface SquareAtInput extends SquareSource, OpenOptions {
70
+ }
71
+ export interface SquareBuildInput extends SquareSource {
72
+ markdown: string;
73
+ hardCap?: number | null;
74
+ throttlePerMinute?: number;
75
+ clock?: () => number;
76
+ notifier?: WakeNotifier;
77
+ }
78
+ export interface Participant {
79
+ readonly name: string;
80
+ express(body: string, options?: ExpressOptions): Promise<ExpressResult>;
81
+ catch(options?: CatchOptions): Promise<CatchResult>;
82
+ history(query?: HistoryQuery): Promise<PerceivedActivity[]>;
83
+ hold(reason?: string): Promise<ExpressResult>;
84
+ resume(): Promise<ExpressResult>;
85
+ done(body?: string): Promise<ExpressResult>;
86
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,18 @@
1
+ import { type BuildOptions, type HardCap } from './model.js';
2
+ import type { OpenSquare } from './open-square.js';
3
+ import type { WakeNotifier } from './square-facade.js';
4
+ /** File-owned artifact creation for the CLI and path-backed public facade. */
5
+ export declare function createSquare(squarePath: string, options: BuildOptions & {
6
+ hardCap: HardCap;
7
+ }, snippet: string): Promise<void>;
8
+ export interface SquareBuildOptions {
9
+ markdown: string;
10
+ hardCap?: number | null;
11
+ throttlePerMinute?: number;
12
+ clock?: () => number;
13
+ notifier?: WakeNotifier;
14
+ }
15
+ export declare function openSquare(squarePath: string, options?: Pick<SquareBuildOptions, 'clock' | 'notifier'>): Promise<OpenSquare>;
16
+ export declare function probeSquare(squarePath: string): OpenSquare | undefined;
17
+ export declare function buildSquare(squarePath: string, options: SquareBuildOptions): Promise<OpenSquare>;
18
+ export declare function buildMemorySquare(options: SquareBuildOptions): OpenSquare;
@@ -0,0 +1,70 @@
1
+ import fs from 'node:fs';
2
+ import { createSquareState, probeSquareFile, writeSquareSnapshot, withSquareFileLock, openSquareCell, createMemoryCell, } from './square-storage.js';
3
+ import { InternalSquareError, SquareError, } from './model.js';
4
+ /** The current CLI file mutation boundary. */
5
+ function writeSquareState(squarePath, squareState) {
6
+ writeSquareSnapshot(squarePath, squareState);
7
+ }
8
+ /** File-owned artifact creation for the CLI and path-backed public facade. */
9
+ export async function createSquare(squarePath, options, snippet) {
10
+ await withSquareFileLock(squarePath, () => {
11
+ if (fs.existsSync(squarePath) && !options.force) {
12
+ throw new InternalSquareError('conflict', `Refusing to overwrite existing square: ${squarePath}\nPass -f to overwrite.`);
13
+ }
14
+ writeSquareSnapshot(squarePath, createSquareState(options, snippet));
15
+ });
16
+ }
17
+ function validateBuildOptions(options) {
18
+ if (options.hardCap !== undefined && options.hardCap !== null
19
+ && (!Number.isSafeInteger(options.hardCap) || options.hardCap < 1)) {
20
+ throw new SquareError('invalid_args', 'hardCap must be a positive integer or null');
21
+ }
22
+ if (options.throttlePerMinute !== undefined
23
+ && (!Number.isSafeInteger(options.throttlePerMinute) || options.throttlePerMinute < 1)) {
24
+ throw new SquareError('invalid_args', 'throttlePerMinute must be a positive integer');
25
+ }
26
+ }
27
+ export async function openSquare(squarePath, options = {}) {
28
+ const cell = openSquareCell(squarePath);
29
+ try {
30
+ await cell.read();
31
+ return { cell, clock: options.clock ?? Date.now, ...(options.notifier === undefined ? {} : { notifier: options.notifier }), location: squarePath };
32
+ }
33
+ catch (error) {
34
+ await cell.close();
35
+ if (error instanceof InternalSquareError && error.code === 'not_found') {
36
+ throw new SquareError('unavailable', `Square is unavailable at ${squarePath}`);
37
+ }
38
+ throw error;
39
+ }
40
+ }
41
+ export function probeSquare(squarePath) {
42
+ const state = probeSquareFile(squarePath);
43
+ return state === undefined ? undefined : { cell: createMemoryCell(state), clock: Date.now, location: squarePath };
44
+ }
45
+ export async function buildSquare(squarePath, options) {
46
+ validateBuildOptions(options);
47
+ try {
48
+ await createSquare(squarePath, {
49
+ force: false,
50
+ hardCap: options.hardCap ?? null,
51
+ ...(options.throttlePerMinute === undefined ? {} : { throttlePerMinute: options.throttlePerMinute }),
52
+ }, options.markdown);
53
+ }
54
+ catch (error) {
55
+ if (error instanceof InternalSquareError && error.code === 'conflict') {
56
+ throw new SquareError('io', `Cannot build over an existing square at ${squarePath}`);
57
+ }
58
+ throw error;
59
+ }
60
+ return openSquare(squarePath, options);
61
+ }
62
+ export function buildMemorySquare(options) {
63
+ validateBuildOptions(options);
64
+ const squareState = createSquareState({
65
+ force: false,
66
+ hardCap: options.hardCap ?? null,
67
+ ...(options.throttlePerMinute === undefined ? {} : { throttlePerMinute: options.throttlePerMinute }),
68
+ }, options.markdown);
69
+ return { cell: createMemoryCell(squareState), clock: options.clock ?? Date.now, ...(options.notifier === undefined ? {} : { notifier: options.notifier }), location: 'memory' };
70
+ }
@@ -0,0 +1,20 @@
1
+ import { createSquareState, diagnoseSquareFile as diagnoseArtifactFile } from './artifact.js';
2
+ import type { StateCell } from './state-cell.js';
3
+ import { type SquareState } from './model.js';
4
+ /**
5
+ * The only production module allowed to cross the .square byte boundary.
6
+ * Consumers receive a SquareState and never need to know which codec or lock
7
+ * protects it.
8
+ */
9
+ export { createSquareState, };
10
+ export declare function readSquareFile(squarePath: string): SquareState;
11
+ export declare function probeSquareFile(squarePath: string): SquareState | undefined;
12
+ export declare function diagnoseSquareFile(squarePath: string): ReturnType<typeof diagnoseArtifactFile>;
13
+ export declare function writeSquareSnapshot(squarePath: string, squareState: SquareState): void;
14
+ export declare function withSquareFileLock<T>(squarePath: string, fn: () => T | Promise<T>): Promise<T>;
15
+ /** In-process cell for fast application tests and embedded consumers. */
16
+ export declare function createMemoryCell(initial: SquareState): StateCell;
17
+ /** File-backed cell retaining the existing .square codec and lock boundary. */
18
+ export declare function createFileCell(squarePath: string): StateCell;
19
+ /** Application-facing file cell factory; keeps storage choice behind this module. */
20
+ export declare function openSquareCell(squarePath: string): StateCell;
@@ -0,0 +1,171 @@
1
+ import { setTimeout as sleep } from 'node:timers/promises';
2
+ import fs from 'node:fs';
3
+ import { createSquareState, diagnoseSquareFile as diagnoseArtifactFile, loadSquare, probeSquare, writeSquareFile, } from './artifact.js';
4
+ import { withFileLock } from './file-lock.js';
5
+ import { LOCK_RETRY_MS, LOCK_STALE_MS } from './runtime.js';
6
+ /**
7
+ * The only production module allowed to cross the .square byte boundary.
8
+ * Consumers receive a SquareState and never need to know which codec or lock
9
+ * protects it.
10
+ */
11
+ export { createSquareState, };
12
+ export function readSquareFile(squarePath) {
13
+ return loadSquare(squarePath);
14
+ }
15
+ export function probeSquareFile(squarePath) {
16
+ return probeSquare(squarePath);
17
+ }
18
+ export function diagnoseSquareFile(squarePath) {
19
+ return diagnoseArtifactFile(squarePath);
20
+ }
21
+ export function writeSquareSnapshot(squarePath, squareState) {
22
+ writeSquareFile(squarePath, squareState);
23
+ }
24
+ export function withSquareFileLock(squarePath, fn) {
25
+ return withFileLock(`${squarePath}.lock`, { retryMs: LOCK_RETRY_MS, staleMs: LOCK_STALE_MS }, fn);
26
+ }
27
+ function cloneState(squareState) {
28
+ return structuredClone(squareState);
29
+ }
30
+ function assertCellOpen(closed) {
31
+ if (closed)
32
+ throw new Error('StateCell is closed');
33
+ }
34
+ /** In-process cell for fast application tests and embedded consumers. */
35
+ export function createMemoryCell(initial) {
36
+ let state = cloneState(initial);
37
+ let version = 0;
38
+ let closed = false;
39
+ let tail = Promise.resolve();
40
+ const waiters = new Set();
41
+ function publish() {
42
+ for (const waiter of [...waiters]) {
43
+ if (version <= waiter.since)
44
+ continue;
45
+ clearTimeout(waiter.timer);
46
+ waiters.delete(waiter);
47
+ waiter.resolve(true);
48
+ }
49
+ }
50
+ const cell = {
51
+ transact(fn) {
52
+ assertCellOpen(closed);
53
+ let operation;
54
+ operation = tail.then(() => {
55
+ assertCellOpen(closed);
56
+ const working = cloneState(state);
57
+ const outcome = fn(working, version);
58
+ if (outcome.state !== undefined) {
59
+ state = cloneState(outcome.state);
60
+ version += 1;
61
+ publish();
62
+ }
63
+ return outcome.result;
64
+ });
65
+ tail = operation.then(() => undefined, () => undefined);
66
+ return operation;
67
+ },
68
+ async read() {
69
+ assertCellOpen(closed);
70
+ await tail;
71
+ return { state: cloneState(state), version };
72
+ },
73
+ changed(sinceVersion, timeoutMs) {
74
+ assertCellOpen(closed);
75
+ if (version > sinceVersion)
76
+ return Promise.resolve(true);
77
+ if (timeoutMs <= 0)
78
+ return Promise.resolve(false);
79
+ return new Promise((resolve) => {
80
+ const waiter = {
81
+ since: sinceVersion,
82
+ resolve,
83
+ timer: setTimeout(() => {
84
+ waiters.delete(waiter);
85
+ resolve(false);
86
+ }, timeoutMs),
87
+ };
88
+ waiters.add(waiter);
89
+ });
90
+ },
91
+ async close() {
92
+ if (closed)
93
+ return;
94
+ closed = true;
95
+ await tail;
96
+ for (const waiter of [...waiters]) {
97
+ clearTimeout(waiter.timer);
98
+ waiters.delete(waiter);
99
+ waiter.resolve(false);
100
+ }
101
+ },
102
+ };
103
+ return cell;
104
+ }
105
+ function fileFingerprint(squarePath) {
106
+ try {
107
+ const stat = fs.statSync(squarePath);
108
+ return `${stat.ino}:${stat.size}:${stat.mtimeMs}:${stat.ctimeMs}`;
109
+ }
110
+ catch {
111
+ return 'missing';
112
+ }
113
+ }
114
+ /** File-backed cell retaining the existing .square codec and lock boundary. */
115
+ export function createFileCell(squarePath) {
116
+ let closed = false;
117
+ let version = 0;
118
+ let fingerprint = fileFingerprint(squarePath);
119
+ function observe() {
120
+ const next = fileFingerprint(squarePath);
121
+ if (next === fingerprint)
122
+ return;
123
+ fingerprint = next;
124
+ version += 1;
125
+ }
126
+ return {
127
+ async transact(fn) {
128
+ assertCellOpen(closed);
129
+ return withFileLock(`${squarePath}.lock`, { retryMs: LOCK_RETRY_MS, staleMs: LOCK_STALE_MS }, () => {
130
+ assertCellOpen(closed);
131
+ observe();
132
+ const current = readSquareFile(squarePath);
133
+ const working = cloneState(current);
134
+ const outcome = fn(working, version);
135
+ if (outcome.state !== undefined) {
136
+ writeSquareSnapshot(squarePath, outcome.state);
137
+ fingerprint = fileFingerprint(squarePath);
138
+ version += 1;
139
+ }
140
+ return outcome.result;
141
+ });
142
+ },
143
+ async read() {
144
+ assertCellOpen(closed);
145
+ observe();
146
+ const state = readSquareFile(squarePath);
147
+ return { state: cloneState(state), version };
148
+ },
149
+ async changed(sinceVersion, timeoutMs) {
150
+ assertCellOpen(closed);
151
+ const deadline = Date.now() + Math.max(0, timeoutMs);
152
+ while (true) {
153
+ observe();
154
+ if (version > sinceVersion)
155
+ return true;
156
+ const remaining = deadline - Date.now();
157
+ if (remaining <= 0)
158
+ return false;
159
+ await sleep(Math.min(25, remaining));
160
+ assertCellOpen(closed);
161
+ }
162
+ },
163
+ async close() {
164
+ closed = true;
165
+ },
166
+ };
167
+ }
168
+ /** Application-facing file cell factory; keeps storage choice behind this module. */
169
+ export function openSquareCell(squarePath) {
170
+ return createFileCell(squarePath);
171
+ }
@@ -0,0 +1,19 @@
1
+ import type { Activity, HistoryQuery, ParticipantStatus, SquareSnapshot } from './square-facade.js';
2
+ import type { Participant, SquareAtInput, SquareBuildInput } from './square-facade.js';
3
+ export declare class Square {
4
+ readonly location: string;
5
+ private readonly square;
6
+ private constructor();
7
+ static at(input: SquareAtInput): Promise<Square>;
8
+ static build(input: SquareBuildInput): Promise<Square>;
9
+ static inMemory(input: Omit<SquareBuildInput, 'path'>): Square;
10
+ join(name: string): Promise<Participant>;
11
+ participants(): Promise<ParticipantStatus[]>;
12
+ snapshot(): Promise<SquareSnapshot>;
13
+ history(query?: HistoryQuery): Promise<Activity[]>;
14
+ close(): Promise<void>;
15
+ }
16
+ export declare function openParticipant(input: SquareAtInput, name: string): Promise<{
17
+ readonly participant: Participant;
18
+ close(): Promise<void>;
19
+ }>;
@@ -0,0 +1,70 @@
1
+ import { closeOpenSquare } from './open-square.js';
2
+ import { buildMemorySquare, buildSquare, openSquare } from './square-file-adapter.js';
3
+ import { done, express, hold, join, resume } from './landing.js';
4
+ import { catchUp } from './presence.js';
5
+ import { history, participantHistory, participants, resolveParticipant, snapshot } from './views.js';
6
+ class ParticipantHandle {
7
+ name;
8
+ square;
9
+ constructor(name, square) {
10
+ this.name = name;
11
+ this.square = square;
12
+ }
13
+ express(body, options) {
14
+ return express(this.square, this.name, body, options);
15
+ }
16
+ catch(options) {
17
+ return catchUp(this.square, this.name, options);
18
+ }
19
+ history(query) {
20
+ return participantHistory(this.square, this.name, query);
21
+ }
22
+ hold(reason) {
23
+ return hold(this.square, this.name, reason);
24
+ }
25
+ resume() {
26
+ return resume(this.square, this.name);
27
+ }
28
+ done(body) {
29
+ return done(this.square, this.name, body);
30
+ }
31
+ }
32
+ export class Square {
33
+ location;
34
+ square;
35
+ constructor(location, square) {
36
+ this.location = location;
37
+ this.square = square;
38
+ }
39
+ static async at(input) {
40
+ return new Square(input.path, await openSquare(input.path, input));
41
+ }
42
+ static async build(input) {
43
+ return new Square(input.path, await buildSquare(input.path, input));
44
+ }
45
+ static inMemory(input) {
46
+ return new Square('memory', buildMemorySquare(input));
47
+ }
48
+ async join(name) {
49
+ const joined = await join(this.square, name);
50
+ return new ParticipantHandle(joined.name, this.square);
51
+ }
52
+ participants() { return participants(this.square); }
53
+ snapshot() { return snapshot(this.square); }
54
+ history(query) { return history(this.square, query); }
55
+ close() { return closeOpenSquare(this.square); }
56
+ }
57
+ export async function openParticipant(input, name) {
58
+ const square = await openSquare(input.path, input);
59
+ try {
60
+ const known = await resolveParticipant(square, name);
61
+ return {
62
+ participant: new ParticipantHandle(known.name, square),
63
+ close: () => closeOpenSquare(square),
64
+ };
65
+ }
66
+ catch (error) {
67
+ await closeOpenSquare(square);
68
+ throw error;
69
+ }
70
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,14 @@
1
+ import type { SquareState } from './model.js';
2
+ /** Synchronous state transition seam shared by memory and file storage. */
3
+ export interface StateCell {
4
+ transact<R>(fn: (state: SquareState, version: number) => {
5
+ state?: SquareState;
6
+ result: R;
7
+ }): Promise<R>;
8
+ read(): Promise<{
9
+ state: SquareState;
10
+ version: number;
11
+ }>;
12
+ changed(sinceVersion: number, timeoutMs: number): Promise<boolean>;
13
+ close(): Promise<void>;
14
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ /** Machine-readable tailing stays available; interactive terminal rendering was retired. */
2
+ export declare function cmdStreamNdjson(squarePath: string, recipient?: string): Promise<void>;
3
+ export declare function cmdStream(squarePath: string): Promise<void>;
package/dist/stream.js CHANGED
@@ -1,27 +1,11 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { setTimeout as sleep } from 'node:timers/promises';
4
- import { loadSquare } from './artifact.js';
5
- import { planActNotifications } from './delivery.js';
6
- import { sameName } from './model.js';
7
4
  import { quoteShell } from './presentation.js';
8
5
  import { SLEEP_MS } from './runtime.js';
9
- export function streamNotificationFor(doc, item, recipient) {
10
- return planActNotifications(doc, item).find((notification) => sameName(notification.recipient, recipient));
11
- }
12
- function streamRows(squarePath, doc, cursor, recipient) {
13
- return doc.acts.filter((act) => act.index > cursor).flatMap((act) => {
14
- const notification = recipient === undefined ? undefined : streamNotificationFor(doc, act, recipient);
15
- if (recipient !== undefined && notification === undefined)
16
- return [];
17
- return [JSON.stringify({
18
- seq: act.index,
19
- square: squarePath,
20
- ...act,
21
- ...(notification === undefined ? {} : { route: notification.route }),
22
- })];
23
- });
24
- }
6
+ import { openSquare } from './square-file-adapter.js';
7
+ import { closeOpenSquare } from './open-square.js';
8
+ import { streamProjection } from './views.js';
25
9
  /** Machine-readable tailing stays available; interactive terminal rendering was retired. */
26
10
  export async function cmdStreamNdjson(squarePath, recipient) {
27
11
  if (!fs.existsSync(squarePath)) {
@@ -32,10 +16,22 @@ export async function cmdStreamNdjson(squarePath, recipient) {
32
16
  let cursor = -1;
33
17
  while (true) {
34
18
  try {
35
- const doc = loadSquare(squarePath);
36
- for (const row of streamRows(squarePath, doc, cursor, recipient))
37
- process.stdout.write(`${row}\n`);
38
- cursor = Math.max(cursor, ...doc.acts.map((act) => act.index));
19
+ const square = await openSquare(squarePath);
20
+ try {
21
+ const projection = await streamProjection(square, cursor, recipient);
22
+ for (const item of projection.activities) {
23
+ process.stdout.write(`${JSON.stringify({
24
+ seq: item.activity.index,
25
+ square: squarePath,
26
+ ...item.activity,
27
+ ...(item.route === undefined ? {} : { route: item.route }),
28
+ })}\n`);
29
+ }
30
+ cursor = projection.cursor;
31
+ }
32
+ finally {
33
+ await closeOpenSquare(square);
34
+ }
39
35
  }
40
36
  catch {
41
37
  // A concurrent artifact replacement is retried on the next poll.
package/dist/time.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ export declare function formatTimestamp(at: number): string;
2
+ export declare function formatDuration(ms: number | undefined): string;
3
+ export declare function formatRelativeTime(at: number, now?: number): string;
4
+ export declare function parseTimestamp(value: string): number;
5
+ /** Relative offsets like -3d, -24h, -30m, -90s; absolute timestamps via parseTimestamp. */
6
+ export declare function parseTimeOrRelative(value: string, now?: number): number;
@@ -0,0 +1,95 @@
1
+ import { perceive, type ActivityId } from './square-core.js';
2
+ import { coreParticipants, coreStatus } from './decisions.js';
3
+ import { type PlannedNotification } from './delivery.js';
4
+ import { type ActivitiesOptions, type InboxNotification, type PublicAct, type RoomChangeAct, type StoredAct } from './model.js';
5
+ import type { OpenSquare } from './open-square.js';
6
+ import type { Activity, HistoryQuery, ParticipantStatus, PerceivedActivity, SquareSnapshot } from './square-facade.js';
7
+ export interface ActivityPresentation {
8
+ readonly name: string;
9
+ readonly roster: readonly string[];
10
+ readonly pendingPublic: readonly PublicAct[];
11
+ readonly pendingRoomChanges: readonly RoomChangeAct[];
12
+ readonly activities: readonly StoredAct[];
13
+ readonly participantCount: number;
14
+ readonly held: boolean;
15
+ readonly holdReason?: string;
16
+ readonly ownActivityCount: number;
17
+ readonly hardCap: number | null;
18
+ }
19
+ export interface EntryPresentation {
20
+ readonly joined: boolean;
21
+ readonly scene: string;
22
+ readonly context: string;
23
+ readonly joinContext: string;
24
+ readonly recentActivities: readonly StoredAct[];
25
+ readonly sayNumbers: Readonly<Record<number, number>>;
26
+ readonly participantCount: number;
27
+ }
28
+ export interface HistoryPresentation {
29
+ readonly activities: readonly (StoredAct & {
30
+ readonly perception: ReturnType<typeof perceive>;
31
+ })[];
32
+ readonly sayNumbers: Readonly<Record<number, number>>;
33
+ readonly presenceAnchors: Readonly<Record<number, readonly string[]>>;
34
+ readonly participantCount: number;
35
+ }
36
+ export interface ListPresentation {
37
+ readonly context: readonly string[];
38
+ readonly participants: readonly string[];
39
+ readonly activities: number;
40
+ }
41
+ export interface StatusPresentation {
42
+ readonly status: ReturnType<typeof coreStatus>;
43
+ readonly latestActNumber?: number;
44
+ }
45
+ export interface WatchPresentation {
46
+ readonly activities: readonly StoredAct[];
47
+ readonly participantCount: number;
48
+ readonly presence: {
49
+ readonly participants: ReturnType<typeof coreParticipants>;
50
+ readonly now: number;
51
+ };
52
+ readonly terminalStatus?: 'capped' | 'quorum';
53
+ }
54
+ export interface InboxProjection {
55
+ readonly name: string;
56
+ readonly joined: boolean;
57
+ readonly notifications: readonly InboxNotification[];
58
+ readonly catchLease?: import('./model.js').WatchLease;
59
+ }
60
+ export interface StreamProjection {
61
+ readonly activities: readonly {
62
+ readonly activity: StoredAct;
63
+ readonly route?: string;
64
+ }[];
65
+ readonly cursor: number;
66
+ }
67
+ export interface PendingDeliveryProjection {
68
+ readonly recipient: string;
69
+ readonly notifications: readonly PlannedNotification[];
70
+ }
71
+ export declare function history(square: OpenSquare, query?: HistoryQuery): Promise<Activity[]>;
72
+ export declare function participantHistory(square: OpenSquare, name: string, query?: HistoryQuery): Promise<PerceivedActivity[]>;
73
+ export declare function resolveParticipant(square: OpenSquare, name: string): Promise<{
74
+ readonly name: string;
75
+ readonly roster: readonly string[];
76
+ }>;
77
+ export declare function participants(square: OpenSquare): Promise<ParticipantStatus[]>;
78
+ export declare function snapshot(square: OpenSquare): Promise<SquareSnapshot>;
79
+ export declare function activityPresentation(square: OpenSquare, name: string): Promise<ActivityPresentation>;
80
+ export declare function entryPresentation(square: OpenSquare, name: string, lastN?: number | null): Promise<EntryPresentation>;
81
+ export declare function historyPresentation(square: OpenSquare, options: ActivitiesOptions): Promise<HistoryPresentation>;
82
+ export declare function participantsPresentation(square: OpenSquare): Promise<ReturnType<typeof coreParticipants>>;
83
+ export declare function listPresentation(square: OpenSquare): Promise<ListPresentation>;
84
+ export declare function statusPresentation(square: OpenSquare): Promise<StatusPresentation>;
85
+ export declare function eventPresentation(square: OpenSquare, id: ActivityId): Promise<{
86
+ readonly activity: StoredAct;
87
+ readonly participantCount: number;
88
+ readonly held: boolean;
89
+ }>;
90
+ export declare function watchPresentation(square: OpenSquare, name: string): Promise<WatchPresentation>;
91
+ export declare function inboxProjection(square: OpenSquare, name: string, ownerId: string): Promise<InboxProjection>;
92
+ export declare function streamProjection(square: OpenSquare, cursor: number, recipient?: string): Promise<StreamProjection>;
93
+ export declare function notificationForAct(square: OpenSquare, actIndex: number): Promise<readonly PlannedNotification[]>;
94
+ export declare function pendingDeliveries(square: OpenSquare): Promise<readonly PendingDeliveryProjection[]>;
95
+ export declare function notificationDelivered(square: OpenSquare, recipient: string, actIndex: number): Promise<boolean>;