@astrosheep/square 0.3.106 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/activity-feed.js +1 -1
- package/dist/artifact-store.d.ts +41 -0
- package/dist/artifact-store.js +272 -0
- package/dist/artifact.d.ts +8 -12
- package/dist/artifact.js +66 -343
- package/dist/attention-presentation.d.ts +33 -7
- package/dist/attention-presentation.js +33 -69
- package/dist/boundary-presentation.d.ts +0 -2
- package/dist/boundary-presentation.js +17 -22
- package/dist/catch-decisions.d.ts +2 -2
- package/dist/catch-decisions.js +14 -4
- package/dist/catch-selection.d.ts +8 -0
- package/dist/catch-selection.js +5 -0
- package/dist/channel-artifact.d.ts +10 -0
- package/dist/channel-artifact.js +55 -0
- package/dist/channel-delivery.d.ts +11 -0
- package/dist/channel-delivery.js +51 -0
- package/dist/channel-domain.d.ts +3 -0
- package/dist/channel-domain.js +83 -0
- package/dist/channel-facade.d.ts +76 -0
- package/dist/channel-facade.js +1 -0
- package/dist/channel-internal.d.ts +126 -0
- package/dist/channel-internal.js +440 -0
- package/dist/channel-metadata.d.ts +11 -0
- package/dist/channel-metadata.js +1 -0
- package/dist/channel-ports.d.ts +241 -0
- package/dist/channel-ports.js +1 -0
- package/dist/channel-presentation.d.ts +17 -0
- package/dist/channel-presentation.js +22 -0
- package/dist/channel-sqlite-schema.d.ts +13 -0
- package/dist/channel-sqlite-schema.js +81 -0
- package/dist/channel-transport.d.ts +62 -0
- package/dist/channel-transport.js +1 -0
- package/dist/channel-values.d.ts +27 -0
- package/dist/channel-values.js +90 -0
- package/dist/channel.d.ts +31 -21
- package/dist/channel.js +42 -74
- package/dist/claude-delivery-status.js +1 -1
- package/dist/claude-delivery.d.ts +2 -5
- package/dist/claude-delivery.js +56 -103
- package/dist/claude-mod.js +4 -4
- package/dist/claude-transport.d.ts +5 -0
- package/dist/claude-transport.js +58 -0
- package/dist/cli/context.d.ts +2 -2
- package/dist/cli/context.js +0 -1
- package/dist/cli/maintenance-commands.d.ts +0 -4
- package/dist/cli/maintenance-commands.js +2 -29
- package/dist/cli/program.js +1 -1
- package/dist/cli/registry.js +1 -2
- package/dist/codex-address.d.ts +1 -1
- package/dist/codex-queue.d.ts +2 -2
- package/dist/codex-queue.js +1 -3
- package/dist/decisions.js +3 -3
- package/dist/delivery-deferral.d.ts +9 -18
- package/dist/delivery-deferral.js +4 -4
- package/dist/delivery-health.d.ts +4 -4
- package/dist/delivery-health.js +11 -11
- package/dist/delivery-operations.d.ts +15 -2
- package/dist/delivery-operations.js +60 -30
- package/dist/delivery.d.ts +44 -33
- package/dist/delivery.js +49 -75
- package/dist/help.js +1 -6
- package/dist/http-delivery.d.ts +2 -2
- package/dist/http-delivery.js +2 -4
- package/dist/inbox.d.ts +10 -18
- package/dist/inbox.js +44 -53
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/mcp.d.ts +2 -1
- package/dist/mcp.js +40 -70
- package/dist/message.d.ts +7 -0
- package/dist/message.js +23 -0
- package/dist/model.d.ts +41 -77
- package/dist/model.js +7 -139
- package/dist/neutral.d.ts +72 -0
- package/dist/neutral.js +27 -0
- package/dist/notifications.d.ts +2 -8
- package/dist/notifications.js +15 -50
- package/dist/operation-presentation.d.ts +1 -2
- package/dist/operation-presentation.js +1 -7
- package/dist/owner.d.ts +2 -2
- package/dist/owner.js +1 -1
- package/dist/paseo-delivery.d.ts +1 -1
- package/dist/paseo-delivery.js +2 -4
- package/dist/plugins/opencode/delivery.js +27 -42
- package/dist/ports.d.ts +46 -168
- package/dist/presentation-operations.d.ts +1 -14
- package/dist/presentation-operations.js +16 -16
- package/dist/presentation.d.ts +5 -5
- package/dist/presentation.js +1 -0
- package/dist/runtime.js +2 -2
- package/dist/search.js +1 -1
- package/dist/sqlite-channel.d.ts +15 -0
- package/dist/sqlite-channel.js +600 -0
- package/dist/sqlite-framing.d.ts +8 -0
- package/dist/sqlite-framing.js +31 -0
- package/dist/sqlite-square.d.ts +12 -13
- package/dist/sqlite-square.js +192 -248
- package/dist/sqlite-values.d.ts +21 -0
- package/dist/sqlite-values.js +78 -0
- package/dist/square-api.d.ts +1 -1
- package/dist/square-api.js +1 -1
- package/dist/square-core.d.ts +1 -1
- package/dist/square-domain.d.ts +21 -7
- package/dist/square-domain.js +93 -94
- package/dist/square-facade.d.ts +12 -66
- package/dist/square-index.d.ts +1 -1
- package/dist/square-index.js +1 -1
- package/dist/square-location.d.ts +6 -1
- package/dist/square-location.js +1 -1
- package/dist/square-receive.d.ts +2 -3
- package/dist/square-sqlite.d.ts +45 -44
- package/dist/square-sqlite.js +308 -764
- package/dist/square-tools.d.ts +8 -0
- package/dist/square-tools.js +39 -0
- package/dist/views.d.ts +6 -6
- package/dist/views.js +15 -9
- package/dist/wake-preview.d.ts +7 -0
- package/dist/wake-preview.js +15 -0
- package/dist/wake-sink.d.ts +2 -2
- package/dist/wake-sink.js +1 -1
- package/dist/wake-transports.d.ts +7 -0
- package/dist/wake-transports.js +42 -0
- package/guides/claude-native.md +13 -10
- package/guides/codex-delivery.md +3 -1
- package/guides/participant-ownership.md +1 -1
- package/guides/row-runtime-delivery-evidence.md +2 -0
- package/guides/row-storage-design.md +19 -44
- package/guides/square-api.md +137 -60
- package/package.json +1 -1
- package/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/plugins/claude/types.d.ts +1 -1
- package/plugins/codex/.codex-plugin/plugin.json +1 -1
- package/plugins/opencode/delivery.ts +28 -41
- package/plugins/pi/square-pi.js +28 -51
- package/dist/square-legacy-framing.d.ts +0 -11
- package/dist/square-legacy-framing.js +0 -64
- package/dist/square-migration-host-ledger.d.ts +0 -26
- package/dist/square-migration-host-ledger.js +0 -169
- package/dist/square-migration.d.ts +0 -30
- package/dist/square-migration.js +0 -599
- package/guides/migration.md +0 -156
package/dist/artifact.js
CHANGED
|
@@ -1,15 +1,27 @@
|
|
|
1
|
-
import { DatabaseSync } from 'node:sqlite';
|
|
2
|
-
import { retrySqliteBusy, inSqliteTransaction } from './sqlite-transaction.js';
|
|
3
|
-
import { randomUUID } from 'node:crypto';
|
|
4
|
-
import fs from 'node:fs';
|
|
5
1
|
import path from 'node:path';
|
|
6
|
-
import
|
|
7
|
-
import { setTimeout as sleep } from 'node:timers/promises';
|
|
8
|
-
import { observeFileVersion } from './file-changes.js';
|
|
2
|
+
import fs from 'node:fs';
|
|
9
3
|
import { canonicalPath } from './canonical-path.js';
|
|
10
4
|
import { InternalSquareError, SquareError } from './model.js';
|
|
11
5
|
import { wrapSquareWrite } from './square-domain.js';
|
|
12
|
-
import {
|
|
6
|
+
import { createSqliteDriver, initializeSqlite, exportSqliteState, inspectSqlite, emptyRuntimeState, squareStoreFormat } from './square-sqlite.js';
|
|
7
|
+
import { createChannelSqlite } from './sqlite-channel.js';
|
|
8
|
+
import { channelLowerSchema } from './channel-sqlite-schema.js';
|
|
9
|
+
import { resetStorageMeta } from './sqlite-framing.js';
|
|
10
|
+
import { createArtifactStore, createStoreFile, replaceStoreFile, inspectStore, observeStoreRevision } from './artifact-store.js';
|
|
11
|
+
import { channelReads, channelWrites } from './channel-artifact.js';
|
|
12
|
+
export { openExistingSquareDatabase } from './artifact-store.js';
|
|
13
|
+
export function channelArtifact(port) {
|
|
14
|
+
return {
|
|
15
|
+
read: (select, signal) => port.read(facts => select(facts.channel), signal),
|
|
16
|
+
transactAs: (authority, change, signal) => port.transactAs(authority, facts => change(facts.channel), signal),
|
|
17
|
+
claimDelivery: (input, signal) => port.claimDelivery(input, signal),
|
|
18
|
+
prepareDelivery: (input, signal) => port.prepareDelivery(input, signal),
|
|
19
|
+
completeDelivery: (input, signal) => port.completeDelivery(input, signal),
|
|
20
|
+
cancelDeliveries: (seat, through, signal) => port.cancelDeliveries(seat, through, signal),
|
|
21
|
+
changed: (since, timeout, signal) => port.changed(since, timeout, signal), close: () => port.close(),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
const format = squareStoreFormat(channelLowerSchema, resetStorageMeta);
|
|
13
25
|
/** Explicit-lifetime holder of one reused artifact connection. Request cancellation
|
|
14
26
|
* never becomes the shared connection's lifetime; close drains the owner once. */
|
|
15
27
|
export function createSquareResourceOwner() {
|
|
@@ -53,26 +65,26 @@ function normalizedLines(value) {
|
|
|
53
65
|
return normalized === '' ? [] : normalized.split('\n');
|
|
54
66
|
}
|
|
55
67
|
/** All build paths share configuration validation and lazy guide preparation. */
|
|
56
|
-
export function prepareSquareBuild(options, snippet) {
|
|
68
|
+
export function prepareSquareBuild(options, snippet, includeGuides = true) {
|
|
57
69
|
if (options.hardCap !== undefined && options.hardCap !== null
|
|
58
70
|
&& (!Number.isSafeInteger(options.hardCap) || options.hardCap < 1))
|
|
59
71
|
throw new SquareError('invalid_args', 'hardCap must be a positive integer or null');
|
|
60
72
|
if (options.throttlePerMinute !== undefined
|
|
61
73
|
&& (!Number.isSafeInteger(options.throttlePerMinute) || options.throttlePerMinute < 1))
|
|
62
74
|
throw new SquareError('invalid_args', 'throttlePerMinute must be a positive integer');
|
|
63
|
-
const guides = [readGuide('participant')];
|
|
64
|
-
if (options.template !== undefined)
|
|
75
|
+
const guides = includeGuides ? [readGuide('participant')] : [];
|
|
76
|
+
if (includeGuides && options.template !== undefined)
|
|
65
77
|
guides.push(readGuide(options.template));
|
|
66
78
|
return {
|
|
67
79
|
hardCap: options.hardCap ?? null,
|
|
68
80
|
...(options.throttlePerMinute === undefined ? {} : { throttlePerMinute: options.throttlePerMinute }),
|
|
69
|
-
preamble: normalizedLines(snippet), warmup: normalizedLines(guides.join('\n\n')),
|
|
81
|
+
preamble: normalizedLines(snippet), warmup: includeGuides ? normalizedLines(guides.join('\n\n')) : [],
|
|
70
82
|
acts: [], runtime: emptyRuntimeState(),
|
|
71
83
|
};
|
|
72
84
|
}
|
|
73
85
|
/** File build owns publication and CLI conflict semantics, not host resources. */
|
|
74
|
-
export async function createSquare(location, options, snippet, signal) {
|
|
75
|
-
const state = prepareSquareBuild(options, snippet);
|
|
86
|
+
export async function createSquare(location, options, snippet, signal, includeGuides = true) {
|
|
87
|
+
const state = prepareSquareBuild(options, snippet, includeGuides);
|
|
76
88
|
if (options.force) {
|
|
77
89
|
await writeSquareFile(location, state, signal);
|
|
78
90
|
}
|
|
@@ -81,359 +93,70 @@ export async function createSquare(location, options, snippet, signal) {
|
|
|
81
93
|
}
|
|
82
94
|
return { hardCap: state.hardCap, ...(state.throttlePerMinute === undefined ? {} : { throttlePerMinute: state.throttlePerMinute }) };
|
|
83
95
|
}
|
|
84
|
-
function requireSquareExtension(location) {
|
|
85
|
-
if (!location.endsWith('.square'))
|
|
86
|
-
throw new SquareError('invalid_args', `Square artifacts must use the .square extension: ${location}`);
|
|
87
|
-
}
|
|
88
|
-
function storageError(error, location) {
|
|
89
|
-
if (!(error instanceof Error) || error instanceof SquareError || error instanceof InternalSquareError)
|
|
90
|
-
return error;
|
|
91
|
-
if (/unable to open database file|no such file|SQLITE_CANTOPEN/i.test(error.message))
|
|
92
|
-
return new InternalSquareError('not_found', `square file not found: ${location}`);
|
|
93
|
-
if (/not a database|SQLITE_NOTADB/i.test(error.message))
|
|
94
|
-
return invalidArtifact('not a SQLite database.');
|
|
95
|
-
return error;
|
|
96
|
-
}
|
|
97
|
-
/** Private artifact-boundary connection shared with explicit migration. Never creates a path. */
|
|
98
|
-
export function openExistingSquareDatabase(location) {
|
|
99
|
-
requireSquareExtension(location);
|
|
100
|
-
const uri = pathToFileURL(path.resolve(location));
|
|
101
|
-
uri.searchParams.set('mode', 'rw');
|
|
102
|
-
try {
|
|
103
|
-
return new DatabaseSync(uri.href);
|
|
104
|
-
}
|
|
105
|
-
catch (error) {
|
|
106
|
-
throw storageError(error, location);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
function abort(signal) {
|
|
110
|
-
if (signal?.aborted)
|
|
111
|
-
throw signal.reason ?? new Error('Square artifact operation aborted');
|
|
112
|
-
}
|
|
113
|
-
function synchronous(value) {
|
|
114
|
-
if (value !== null && (typeof value === 'object' || typeof value === 'function') && typeof value.then === 'function') {
|
|
115
|
-
void Promise.resolve(value).catch(() => undefined);
|
|
116
|
-
throw new TypeError('Square artifact callbacks must be synchronous.');
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
96
|
const readMethods = [
|
|
120
97
|
'header', 'participant', 'roster', 'attention', 'incomingAttention',
|
|
121
|
-
'activity', 'activityHeads', 'activities', 'activityCount', 'contextIndexes', 'sayOrdinals', 'recentSayTimes',
|
|
122
|
-
'audience', 'directed', 'directedAt', 'directedCount',
|
|
98
|
+
'activity', 'activityByMessage', 'activityHeads', 'activities', 'activityCount', 'contextIndexes', 'sayOrdinals', 'recentSayTimes',
|
|
99
|
+
'audience', 'directed', 'directedAt', 'directedCount',
|
|
123
100
|
'receipt', 'receiptFacts', 'lease', 'participantCounts', 'seat', 'seatsForOwner',
|
|
124
101
|
'deliveryEvidence', 'deliveryByToken', 'cancelledThrough', 'deliveryDeferrals',
|
|
125
102
|
];
|
|
126
|
-
const writeMethods = ['applyLanding', '
|
|
127
|
-
/**
|
|
103
|
+
const writeMethods = ['applyLanding', 'putLease', 'removeLease', 'storeSeat', 'removeSeat', 'updateWakeAddress', 'reattachSeat', 'detachSeat'];
|
|
104
|
+
/** Agora composition over the same lower SQL and ordered store used by standalone Channel. */
|
|
128
105
|
export function createArtifactPort(location, initial, externalSignal) {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
initializeSqlite(database, initial);
|
|
138
|
-
database.exec(`PRAGMA application_id=${APPLICATION_ID}; PRAGMA user_version=${CURRENT_USER_VERSION}`);
|
|
139
|
-
}
|
|
140
|
-
catch (error) {
|
|
141
|
-
database.close();
|
|
142
|
-
throw error;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
const combined = (signal) => AbortSignal.any([cancel.signal, ...(externalSignal ? [externalSignal] : []), ...(signal ? [signal] : [])]);
|
|
146
|
-
const validate = (db) => validateSqliteHeader(db, initial === undefined ? location : ':memory:');
|
|
147
|
-
async function connection(signal) {
|
|
148
|
-
if (database)
|
|
149
|
-
return database;
|
|
150
|
-
location = await canonical;
|
|
151
|
-
abort(signal);
|
|
152
|
-
database = openExistingSquareDatabase(location);
|
|
153
|
-
try {
|
|
154
|
-
await retrySqliteBusy(() => validate(database), signal);
|
|
155
|
-
database.exec('PRAGMA busy_timeout=0; PRAGMA synchronous=FULL');
|
|
156
|
-
}
|
|
157
|
-
catch (error) {
|
|
158
|
-
database.close();
|
|
159
|
-
database = undefined;
|
|
160
|
-
throw storageError(error, location);
|
|
161
|
-
}
|
|
162
|
-
return database;
|
|
163
|
-
}
|
|
164
|
-
function queue(operation, operationSignal) {
|
|
165
|
-
if (inCallback)
|
|
166
|
-
throw new TypeError('Square artifact operations cannot nest inside a callback.');
|
|
167
|
-
if (closed)
|
|
168
|
-
return Promise.reject(new Error('Square artifact is closed'));
|
|
169
|
-
const result = tail.then(() => {
|
|
170
|
-
abort(operationSignal);
|
|
171
|
-
return operation(operationSignal);
|
|
172
|
-
});
|
|
173
|
-
tail = result.then(() => undefined, () => undefined);
|
|
174
|
-
return result;
|
|
175
|
-
}
|
|
176
|
-
async function transaction(callback, write, signal, authority) {
|
|
177
|
-
const db = await connection(signal);
|
|
178
|
-
return inSqliteTransaction(db, write, () => validate(db), (header) => {
|
|
179
|
-
const driver = createSqliteDriver(db), version = header.revision;
|
|
180
|
-
let active = true, failed = false, failure;
|
|
181
|
-
function guard(fn) {
|
|
182
|
-
return ((...args) => {
|
|
183
|
-
if (!active)
|
|
184
|
-
throw new Error('Square artifact transaction capability expired');
|
|
185
|
-
abort(signal);
|
|
186
|
-
if (failed)
|
|
187
|
-
throw failure;
|
|
188
|
-
try {
|
|
189
|
-
return fn(...args);
|
|
190
|
-
}
|
|
191
|
-
catch (error) {
|
|
192
|
-
failed = true;
|
|
193
|
-
failure = storageError(error, location);
|
|
194
|
-
throw failure;
|
|
195
|
-
}
|
|
196
|
-
});
|
|
197
|
-
}
|
|
198
|
-
const reads = Object.fromEntries(readMethods.map(name => [name, guard(driver[name])]));
|
|
199
|
-
const writes = Object.fromEntries(writeMethods.map(name => [name, guard(driver[name])]));
|
|
106
|
+
const store = createArtifactStore(location, format, initial === undefined ? undefined : db => initializeSqlite(db, initial, 0, channelLowerSchema), externalSignal);
|
|
107
|
+
function transaction(select, write, authority, signal, settlement = false) {
|
|
108
|
+
return store.run(write, ({ db, guard, track }) => {
|
|
109
|
+
const channel = createChannelSqlite(db);
|
|
110
|
+
const driver = createSqliteDriver(db, channel);
|
|
111
|
+
track(driver);
|
|
112
|
+
const reads = Object.assign(Object.fromEntries(readMethods.map(name => [name, guard(driver[name])])), { channel: channelReads(channel, guard) });
|
|
113
|
+
const writes = Object.assign(Object.fromEntries(writeMethods.map(name => [name, guard(driver[name])])), { channel: channelWrites(channel, guard) });
|
|
200
114
|
const gate = write ? wrapSquareWrite(reads, writes, authority) : undefined;
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
if (failed)
|
|
215
|
-
throw failure;
|
|
216
|
-
const changed = write && driver.changed;
|
|
217
|
-
const deliveryChanged = write && driver.deliveryChanged;
|
|
218
|
-
if (changed || deliveryChanged) {
|
|
219
|
-
if (version === Number.MAX_SAFE_INTEGER)
|
|
220
|
-
throw invalidArtifact('revision cannot advance beyond the safe integer limit.');
|
|
221
|
-
db.prepare('UPDATE square_meta SET revision=revision+?, delivery_revision=delivery_revision+? WHERE id=1').run(Number(changed), Number(deliveryChanged));
|
|
222
|
-
}
|
|
223
|
-
return {
|
|
224
|
-
value, version: version + Number(changed), changed, deliveryChanged
|
|
225
|
-
};
|
|
226
|
-
}, signal);
|
|
227
|
-
}
|
|
228
|
-
const revision = signal => queue(async (s) => {
|
|
229
|
-
const db = await connection(s);
|
|
230
|
-
return inSqliteTransaction(db, false, () => validate(db), (header) => header.revision, s);
|
|
231
|
-
}, combined(signal));
|
|
232
|
-
return {
|
|
233
|
-
read: (select, signal) => queue(async (s) => {
|
|
234
|
-
const { value, version } = await transaction(select, false, s);
|
|
235
|
-
return { value, version };
|
|
236
|
-
}, combined(signal)),
|
|
237
|
-
transactAs: (authority, change, signal) => queue(s => transaction(change, true, s, authority), combined(signal)),
|
|
238
|
-
// Keep the acquired connection and authority, not the cancelled work lifetime. A
|
|
239
|
-
// blocked evidence commit/cleanup gets five seconds, and close still cancels it.
|
|
240
|
-
settleAs: (authority, change) => queue(s => transaction(change, true, s, authority), AbortSignal.any([cancel.signal, AbortSignal.timeout(5000)])),
|
|
241
|
-
revision,
|
|
242
|
-
async deliveryRevision(signal) {
|
|
243
|
-
return queue(async (s) => {
|
|
244
|
-
const db = await connection(s);
|
|
245
|
-
return inSqliteTransaction(db, false, () => validate(db), (header) => header.deliveryRevision, s);
|
|
246
|
-
}, combined(signal));
|
|
247
|
-
},
|
|
248
|
-
async claimDelivery(input, signal) {
|
|
249
|
-
return (await this.transactAs({ kind: 'participant', seat: input.seat }, (facts) => facts.claimDelivery(input), signal)).value;
|
|
250
|
-
},
|
|
251
|
-
async prepareDelivery(input, signal) {
|
|
252
|
-
return (await this.transactAs({ kind: 'participant', seat: input.seat }, facts => facts.prepareDelivery(input), signal)).value;
|
|
253
|
-
},
|
|
254
|
-
async completeDelivery(input, signal) {
|
|
255
|
-
return (await this.transactAs({ kind: 'participant', seat: input.seat }, facts => facts.completeDelivery(input), signal)).value;
|
|
256
|
-
},
|
|
115
|
+
const value = select(gate?.facts ?? reads);
|
|
116
|
+
gate?.complete();
|
|
117
|
+
return value;
|
|
118
|
+
}, signal, settlement);
|
|
119
|
+
}
|
|
120
|
+
const port = {
|
|
121
|
+
read: (select, signal) => transaction(select, false, undefined, signal),
|
|
122
|
+
transactAs: (authority, change, signal) => transaction(change, true, authority, signal),
|
|
123
|
+
settleAs: (authority, change) => transaction(change, true, authority, undefined, true),
|
|
124
|
+
revision: store.revision, deliveryRevision: store.deliveryRevision,
|
|
125
|
+
async claimDelivery(input, signal) { return (await port.transactAs({ kind: 'participant', seat: input.seat }, facts => facts.channel.claimDelivery(input), signal)).value; },
|
|
126
|
+
async prepareDelivery(input, signal) { return (await port.transactAs({ kind: 'participant', seat: input.seat }, facts => facts.channel.prepareDelivery(input), signal)).value; },
|
|
127
|
+
async completeDelivery(input, signal) { return (await port.transactAs({ kind: 'participant', seat: input.seat }, facts => facts.channel.completeDelivery(input), signal)).value; },
|
|
257
128
|
async cancelDeliveries(seat, through, signal) {
|
|
258
|
-
return (await
|
|
259
|
-
const boundary = through ?? facts.header().
|
|
260
|
-
return boundary < 0 ? false : facts.cancelThrough(seat, boundary);
|
|
129
|
+
return (await port.transactAs({ kind: 'participant', seat }, facts => {
|
|
130
|
+
const boundary = through ?? facts.channel.header().lastMessageIndex;
|
|
131
|
+
return boundary < 0 ? false : facts.channel.cancelThrough(seat, boundary);
|
|
261
132
|
}, signal)).value;
|
|
262
133
|
},
|
|
263
|
-
|
|
264
|
-
if (inCallback)
|
|
265
|
-
throw new TypeError('Square artifact operations cannot nest inside a callback.');
|
|
266
|
-
if (closed)
|
|
267
|
-
return false;
|
|
268
|
-
const s = combined(signal), deadline = Date.now() + Math.max(0, timeout);
|
|
269
|
-
if (initial !== undefined) {
|
|
270
|
-
try {
|
|
271
|
-
do {
|
|
272
|
-
if (await revision(s) > since)
|
|
273
|
-
return true;
|
|
274
|
-
if (Date.now() >= deadline)
|
|
275
|
-
return false;
|
|
276
|
-
await sleep(Math.min(25, deadline - Date.now()), undefined, { signal: s });
|
|
277
|
-
} while (!closed);
|
|
278
|
-
}
|
|
279
|
-
catch (error) {
|
|
280
|
-
if (closed)
|
|
281
|
-
return false;
|
|
282
|
-
abort(s);
|
|
283
|
-
throw error;
|
|
284
|
-
}
|
|
285
|
-
return false;
|
|
286
|
-
}
|
|
287
|
-
const observer = await observeSquareRevision(await canonical);
|
|
288
|
-
try {
|
|
289
|
-
return await observer.changed(since, Math.max(0, deadline - Date.now()), s);
|
|
290
|
-
}
|
|
291
|
-
catch (error) {
|
|
292
|
-
if (closed)
|
|
293
|
-
return false;
|
|
294
|
-
throw error;
|
|
295
|
-
}
|
|
296
|
-
finally {
|
|
297
|
-
observer.close();
|
|
298
|
-
}
|
|
299
|
-
},
|
|
300
|
-
async close() {
|
|
301
|
-
if (inCallback)
|
|
302
|
-
throw new TypeError('Square artifact cannot close inside a callback.');
|
|
303
|
-
if (closed) {
|
|
304
|
-
await tail;
|
|
305
|
-
return;
|
|
306
|
-
}
|
|
307
|
-
closed = true;
|
|
308
|
-
cancel.abort(new Error('Square artifact is closed'));
|
|
309
|
-
await tail;
|
|
310
|
-
database?.close();
|
|
311
|
-
database = undefined;
|
|
312
|
-
},
|
|
134
|
+
changed: store.changed, close: store.close,
|
|
313
135
|
};
|
|
136
|
+
return port;
|
|
314
137
|
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
const temporary = path.join(path.dirname(location), `.${path.basename(location)}.${process.pid}.${randomUUID()}.tmp`);
|
|
318
|
-
const handle = await fs.promises.open(temporary, 'wx');
|
|
319
|
-
await handle.close();
|
|
320
|
-
return temporary;
|
|
321
|
-
}
|
|
322
|
-
async function initializeFile(location, state, signal) {
|
|
323
|
-
const db = new DatabaseSync(location);
|
|
324
|
-
try {
|
|
325
|
-
db.exec('PRAGMA journal_mode=DELETE; PRAGMA synchronous=FULL');
|
|
326
|
-
await inSqliteTransaction(db, true, () => { }, () => {
|
|
327
|
-
initializeSqlite(db, state);
|
|
328
|
-
db.exec(`PRAGMA application_id=${APPLICATION_ID}; PRAGMA user_version=${CURRENT_USER_VERSION}`);
|
|
329
|
-
}, signal);
|
|
330
|
-
}
|
|
331
|
-
finally {
|
|
332
|
-
db.close();
|
|
333
|
-
}
|
|
334
|
-
const handle = await fs.promises.open(location, 'r+');
|
|
335
|
-
try {
|
|
336
|
-
await handle.sync();
|
|
337
|
-
}
|
|
338
|
-
finally {
|
|
339
|
-
await handle.close();
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
/** New-format creation; initial authority is accepted only at this explicit boundary. */
|
|
343
|
-
export async function createSquareFile(location, state, signal) {
|
|
344
|
-
abort(signal);
|
|
345
|
-
location = await canonicalPath(location);
|
|
346
|
-
requireSquareExtension(location);
|
|
347
|
-
const temporary = await temporaryArtifact(location);
|
|
348
|
-
try {
|
|
349
|
-
await initializeFile(temporary, state, signal);
|
|
350
|
-
abort(signal);
|
|
351
|
-
try {
|
|
352
|
-
await fs.promises.link(temporary, location);
|
|
353
|
-
return true;
|
|
354
|
-
}
|
|
355
|
-
catch (error) {
|
|
356
|
-
if (error.code === 'EEXIST')
|
|
357
|
-
return false;
|
|
358
|
-
throw error;
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
finally {
|
|
362
|
-
await fs.promises.unlink(temporary).catch(() => undefined);
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
/** Explicit build --force / fixture import, never a runtime state-replacement callback. */
|
|
366
|
-
export async function writeSquareFile(location, state, signal) {
|
|
367
|
-
location = await canonicalPath(location);
|
|
368
|
-
if (await createSquareFile(location, state, signal))
|
|
369
|
-
return;
|
|
370
|
-
abort(signal);
|
|
371
|
-
const db = openExistingSquareDatabase(location);
|
|
372
|
-
try {
|
|
373
|
-
await retrySqliteBusy(() => validateSqliteHeader(db, location), signal);
|
|
374
|
-
db.exec('PRAGMA busy_timeout=0; PRAGMA synchronous=FULL');
|
|
375
|
-
await inSqliteTransaction(db, true, () => validateSqliteHeader(db, location), () => {
|
|
376
|
-
const revision = createSqliteDriver(db).header().revision;
|
|
377
|
-
if (revision === Number.MAX_SAFE_INTEGER)
|
|
378
|
-
throw invalidArtifact('revision cannot advance beyond the safe integer limit.');
|
|
379
|
-
for (const table of ['landed_audience', 'attention', 'participants', 'delivery_deferrals', 'delivery_cancellations', 'delivery_evidence', 'seats', 'catch_leases', 'receipts', 'activities', 'square_meta'])
|
|
380
|
-
db.exec(`DROP TABLE ${table}`);
|
|
381
|
-
initializeSqlite(db, state, revision + 1);
|
|
382
|
-
}, signal);
|
|
383
|
-
}
|
|
384
|
-
finally {
|
|
385
|
-
db.close();
|
|
386
|
-
}
|
|
138
|
+
export function createSquareFile(location, state, signal) {
|
|
139
|
+
return createStoreFile(location, format, db => initializeSqlite(db, state, 0, channelLowerSchema), signal);
|
|
387
140
|
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
try {
|
|
391
|
-
return await inSqliteTransaction(db, false, () => validateSqliteHeader(db, location), () => select(db), signal);
|
|
392
|
-
}
|
|
393
|
-
catch (error) {
|
|
394
|
-
throw storageError(error, location);
|
|
395
|
-
}
|
|
396
|
-
finally {
|
|
397
|
-
db.close();
|
|
398
|
-
}
|
|
141
|
+
export function writeSquareFile(location, state, signal) {
|
|
142
|
+
return replaceStoreFile(location, format, (db, revision) => initializeSqlite(db, state, revision, channelLowerSchema), signal);
|
|
399
143
|
}
|
|
400
|
-
/** Explicit test/export projection. Hot callers must use the typed port. */
|
|
401
144
|
export function readSquareSnapshot(location, signal) {
|
|
402
|
-
return
|
|
145
|
+
return inspectStore(location, format, db => ({ state: exportSqliteState(db), revision: createSqliteDriver(db).header().revision }), signal);
|
|
403
146
|
}
|
|
404
147
|
export function readSquareRevision(location, signal) {
|
|
405
|
-
return
|
|
148
|
+
return inspectStore(location, format, db => createSqliteDriver(db).header().revision, signal);
|
|
406
149
|
}
|
|
407
150
|
export function readSquareDeliveryRevision(location, signal) {
|
|
408
|
-
return
|
|
409
|
-
}
|
|
410
|
-
function observeRevision(location, revision) {
|
|
411
|
-
const read = revision === 'square-revision' ? readSquareRevision : readSquareDeliveryRevision;
|
|
412
|
-
return observeFileVersion(location, revision, async (file, signal) => {
|
|
413
|
-
try {
|
|
414
|
-
return await read(file, signal);
|
|
415
|
-
}
|
|
416
|
-
catch (error) {
|
|
417
|
-
abort(signal);
|
|
418
|
-
if (error instanceof InternalSquareError && error.code === 'not_found')
|
|
419
|
-
return null;
|
|
420
|
-
throw error;
|
|
421
|
-
}
|
|
422
|
-
});
|
|
423
|
-
}
|
|
424
|
-
export function observeSquareRevision(location) {
|
|
425
|
-
return observeRevision(location, 'square-revision');
|
|
426
|
-
}
|
|
427
|
-
export function observeSquareDeliveryRevision(location) {
|
|
428
|
-
return observeRevision(location, 'square-delivery-revision');
|
|
151
|
+
return inspectStore(location, format, db => createSqliteDriver(db).header().deliveryRevision, signal);
|
|
429
152
|
}
|
|
153
|
+
export const observeSquareRevision = (location) => observeStoreRevision(location, format);
|
|
154
|
+
export const observeSquareDeliveryRevision = (location) => observeStoreRevision(location, format, true);
|
|
430
155
|
export async function diagnoseSquareFile(location) {
|
|
431
156
|
try {
|
|
432
|
-
return { problems: [], ...await
|
|
157
|
+
return { problems: [], ...await inspectStore(await canonicalPath(location), format, db => inspectSqlite(db, channelLowerSchema)) };
|
|
433
158
|
}
|
|
434
159
|
catch (error) {
|
|
435
|
-
if (error instanceof SquareError && error.facts?.reason === 'migration_required')
|
|
436
|
-
return { migrationRequired: error.message, problems: [] };
|
|
437
160
|
return { unfixable: error instanceof Error ? error.message : String(error), problems: [] };
|
|
438
161
|
}
|
|
439
162
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
export { projectChannelNotification, renderSquareReminder } from './channel-presentation.js';
|
|
2
|
+
import type { DirectedNotificationRoute } from './model.js';
|
|
2
3
|
export declare const ATTENTION_BODY_MAX = 200;
|
|
3
4
|
export interface AttentionPreview {
|
|
4
5
|
squarePath: string;
|
|
@@ -7,14 +8,39 @@ export interface AttentionPreview {
|
|
|
7
8
|
actor: string;
|
|
8
9
|
route: DirectedNotificationRoute;
|
|
9
10
|
body: string;
|
|
10
|
-
|
|
11
|
+
preview?: string;
|
|
11
12
|
}
|
|
12
|
-
export declare function previewAttentionBody(body: string
|
|
13
|
-
export declare function attentionBodyIsClipped(body: string
|
|
13
|
+
export declare function previewAttentionBody(body: string): string;
|
|
14
|
+
export declare function attentionBodyIsClipped(body: string): boolean;
|
|
14
15
|
export declare function displayAttentionPath(squarePath: string): string;
|
|
15
16
|
export declare function renderAttentionDescription({ actor, route }: Pick<AttentionPreview, 'actor' | 'route'>): string;
|
|
16
|
-
/**
|
|
17
|
+
/** The sole source adapter: transports and cards consume this projection, not delivery modes. */
|
|
18
|
+
export declare function projectNotification(attention: AttentionPreview): {
|
|
19
|
+
text: string;
|
|
20
|
+
full: string;
|
|
21
|
+
preview: string;
|
|
22
|
+
description: string;
|
|
23
|
+
clipped: boolean;
|
|
24
|
+
agora: boolean;
|
|
25
|
+
};
|
|
26
|
+
/** One stored message: Agora framing when an upper say owns it, Channel's raw frame otherwise. */
|
|
27
|
+
export declare function projectMessageNotification(input: {
|
|
28
|
+
squarePath: string;
|
|
29
|
+
recipient: string;
|
|
30
|
+
actor: string;
|
|
31
|
+
body: string;
|
|
32
|
+
preview?: string;
|
|
33
|
+
agora?: {
|
|
34
|
+
readonly actIndex: number;
|
|
35
|
+
readonly route: DirectedNotificationRoute;
|
|
36
|
+
};
|
|
37
|
+
}): {
|
|
38
|
+
text: string;
|
|
39
|
+
full: string;
|
|
40
|
+
preview: string;
|
|
41
|
+
description: string;
|
|
42
|
+
clipped: boolean;
|
|
43
|
+
};
|
|
44
|
+
/** Full human expansion never substitutes a sender-owned preview for the stored body. */
|
|
17
45
|
export declare function renderAttentionFull(attention: AttentionPreview): string;
|
|
18
|
-
/** Shared model-facing envelope; transport correlation and human cards stay outside it. */
|
|
19
|
-
export declare function renderSquareReminder(previews: readonly string[], deliveries?: readonly (ActivityDelivery | undefined)[]): string;
|
|
20
46
|
export declare function renderAttentionPreview(attention: AttentionPreview): string;
|
|
@@ -1,21 +1,17 @@
|
|
|
1
|
+
import { escapeSquareTags, projectChannelNotification } from './channel-presentation.js';
|
|
2
|
+
export { projectChannelNotification, renderSquareReminder } from './channel-presentation.js';
|
|
1
3
|
import { homedir } from 'node:os';
|
|
2
4
|
import { formatActivityId } from './square-core.js';
|
|
3
5
|
export const ATTENTION_BODY_MAX = 200;
|
|
4
|
-
export function previewAttentionBody(body
|
|
5
|
-
const compact =
|
|
6
|
-
|
|
7
|
-
return compact;
|
|
8
|
-
return `${compact.slice(0, ATTENTION_BODY_MAX).trimEnd()}…`;
|
|
6
|
+
export function previewAttentionBody(body) {
|
|
7
|
+
const compact = body.replace(/\r\n/g, '\n');
|
|
8
|
+
return compact.length <= ATTENTION_BODY_MAX ? compact : `${compact.slice(0, ATTENTION_BODY_MAX).trimEnd()}…`;
|
|
9
9
|
}
|
|
10
|
-
export function attentionBodyIsClipped(body
|
|
11
|
-
return
|
|
12
|
-
? delivery.preview !== undefined
|
|
13
|
-
: body.replace(/\r\n/g, '\n').length > ATTENTION_BODY_MAX;
|
|
10
|
+
export function attentionBodyIsClipped(body) {
|
|
11
|
+
return body.replace(/\r\n/g, '\n').length > ATTENTION_BODY_MAX;
|
|
14
12
|
}
|
|
15
13
|
export function displayAttentionPath(squarePath) {
|
|
16
|
-
return squarePath.startsWith(homedir())
|
|
17
|
-
? `~${squarePath.slice(homedir().length)}`
|
|
18
|
-
: squarePath;
|
|
14
|
+
return squarePath.startsWith(homedir()) ? `~${squarePath.slice(homedir().length)}` : squarePath;
|
|
19
15
|
}
|
|
20
16
|
export function renderAttentionDescription({ actor, route }) {
|
|
21
17
|
switch (route) {
|
|
@@ -25,56 +21,15 @@ export function renderAttentionDescription({ actor, route }) {
|
|
|
25
21
|
case 'bell': return `${actor} rang the bell`;
|
|
26
22
|
}
|
|
27
23
|
}
|
|
28
|
-
/** Full notification text for human-facing expansion, never the bounded model preview. */
|
|
29
|
-
export function renderAttentionFull(attention) {
|
|
30
|
-
return attention.delivery?.mode === 'direct' ? renderDirectBlock(attention) : renderAttentionBlock(attention, attention.body);
|
|
31
|
-
}
|
|
32
|
-
/** Attribute values keep their quotes; the stored body and the human card stay unescaped. */
|
|
33
24
|
function escapeAttribute(value) {
|
|
34
|
-
return value
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
return text.replace(/<(\/?)(square-|system-reminder)/gi, '<\\$1$2');
|
|
43
|
-
}
|
|
44
|
-
function renderAttentionBlock(attention, body) {
|
|
45
|
-
return [
|
|
46
|
-
'<square-activity>',
|
|
47
|
-
`· ${displayAttentionPath(attention.squarePath)} · ${attention.recipient} · ${formatActivityId(attention.actIndex)}`,
|
|
48
|
-
`● ${renderAttentionDescription(attention)}`,
|
|
49
|
-
'',
|
|
50
|
-
body,
|
|
51
|
-
'</square-activity>',
|
|
52
|
-
].join('\n');
|
|
53
|
-
}
|
|
54
|
-
function renderDirectBlock(attention) {
|
|
55
|
-
return escapeSquareTags(previewAttentionBody(attention.body, attention.delivery));
|
|
56
|
-
}
|
|
57
|
-
/** Shared model-facing envelope; transport correlation and human cards stay outside it. */
|
|
58
|
-
export function renderSquareReminder(previews, deliveries = []) {
|
|
59
|
-
if (previews.length === 0)
|
|
60
|
-
return '';
|
|
61
|
-
const header = deliveries.some(item => item?.mode === 'direct')
|
|
62
|
-
? 'This is an external notification, not a direct message from the user.'
|
|
63
|
-
: 'This is an external reminder from Square, not a direct message from the user.';
|
|
64
|
-
return [
|
|
65
|
-
'',
|
|
66
|
-
'<system-reminder>',
|
|
67
|
-
header,
|
|
68
|
-
'',
|
|
69
|
-
previews.join('\n\n'),
|
|
70
|
-
'</system-reminder>',
|
|
71
|
-
'',
|
|
72
|
-
].join('\n');
|
|
73
|
-
}
|
|
74
|
-
export function renderAttentionPreview(attention) {
|
|
75
|
-
if (attention.delivery?.mode === 'direct')
|
|
76
|
-
return renderDirectBlock(attention);
|
|
77
|
-
const remaining = attention.body.replace(/\r\n/g, '\n').length - ATTENTION_BODY_MAX;
|
|
25
|
+
return value.replace(/&/g, '&').replace(/"/g, '"').replace(/</g, '<').replace(/>/g, '>');
|
|
26
|
+
}
|
|
27
|
+
/** The sole source adapter: transports and cards consume this projection, not delivery modes. */
|
|
28
|
+
export function projectNotification(attention) {
|
|
29
|
+
const body = attention.body.replace(/\r\n/g, '\n');
|
|
30
|
+
const description = renderAttentionDescription(attention);
|
|
31
|
+
const remaining = body.length - ATTENTION_BODY_MAX;
|
|
32
|
+
const preview = previewAttentionBody(body);
|
|
78
33
|
const attributes = [
|
|
79
34
|
`square="${escapeAttribute(displayAttentionPath(attention.squarePath))}"`,
|
|
80
35
|
`as="${escapeAttribute(attention.recipient)}"`,
|
|
@@ -82,11 +37,20 @@ export function renderAttentionPreview(attention) {
|
|
|
82
37
|
`from="${escapeAttribute(attention.actor)}"`,
|
|
83
38
|
...(remaining > 0 ? [`truncated="${remaining} more chars"`] : []),
|
|
84
39
|
];
|
|
85
|
-
return
|
|
86
|
-
`<square-activity ${attributes.join(' ')}>`,
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
40
|
+
return {
|
|
41
|
+
text: [`<square-activity ${attributes.join(' ')}>`, `${description}.`, '', escapeSquareTags(preview), '</square-activity>'].join('\n'),
|
|
42
|
+
full: ['<square-activity>', `· ${displayAttentionPath(attention.squarePath)} · ${attention.recipient} · ${formatActivityId(attention.actIndex)}`,
|
|
43
|
+
`● ${description}`, '', body, '</square-activity>'].join('\n'),
|
|
44
|
+
preview, description, clipped: remaining > 0, agora: true,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/** One stored message: Agora framing when an upper say owns it, Channel's raw frame otherwise. */
|
|
48
|
+
export function projectMessageNotification(input) {
|
|
49
|
+
if (input.agora === undefined)
|
|
50
|
+
return projectChannelNotification(input);
|
|
51
|
+
return projectNotification({ squarePath: input.squarePath, recipient: input.recipient, actor: input.actor,
|
|
52
|
+
route: input.agora.route, actIndex: input.agora.actIndex, body: input.body });
|
|
53
|
+
}
|
|
54
|
+
/** Full human expansion never substitutes a sender-owned preview for the stored body. */
|
|
55
|
+
export function renderAttentionFull(attention) { return projectNotification(attention).full; }
|
|
56
|
+
export function renderAttentionPreview(attention) { return projectNotification(attention).text; }
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import { pendingAtBoundary } from './inbox.js';
|
|
2
1
|
import type { Owner, PendingMembership } from './model.js';
|
|
3
|
-
export { pendingAtBoundary };
|
|
4
2
|
export declare function renderPendingAtBoundary(inbox: PendingMembership[]): string;
|
|
5
3
|
/**
|
|
6
4
|
* Serialize a boundary across hook processes so one stale inbox cannot render twice.
|