@astrosheep/square 0.3.106 → 0.4.1
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 +6 -12
- package/dist/artifact.js +43 -344
- package/dist/attention-presentation.d.ts +13 -7
- package/dist/attention-presentation.js +29 -68
- package/dist/boundary-presentation.d.ts +3 -16
- package/dist/boundary-presentation.js +29 -109
- package/dist/card-presentation.d.ts +1 -0
- package/dist/card-presentation.js +2 -0
- package/dist/catch-decisions.d.ts +2 -2
- package/dist/catch-decisions.js +7 -3
- package/dist/catch-selection.d.ts +8 -0
- package/dist/catch-selection.js +5 -0
- package/dist/claude-delivery.d.ts +14 -12
- package/dist/claude-delivery.js +42 -142
- package/dist/claude-mcp-context.js +2 -2
- package/dist/claude-mod.js +28 -140
- package/dist/claude-transport.d.ts +5 -0
- package/dist/claude-transport.js +34 -0
- package/dist/cli/context.d.ts +2 -2
- package/dist/cli/context.js +7 -10
- package/dist/cli/maintenance-commands.d.ts +0 -4
- package/dist/cli/maintenance-commands.js +2 -29
- package/dist/cli/observation-commands.d.ts +2 -0
- package/dist/cli/observation-commands.js +12 -2
- package/dist/cli/program.js +1 -1
- package/dist/cli/registry.js +4 -3
- package/dist/codex-address.d.ts +2 -2
- package/dist/codex-boundary-state.d.ts +1 -5
- package/dist/codex-boundary-state.js +0 -4
- package/dist/codex-hook.d.ts +7 -10
- package/dist/codex-hook.js +69 -33
- package/dist/codex-queue.d.ts +10 -11
- package/dist/codex-queue.js +32 -34
- package/dist/codex-watch.d.ts +2 -0
- package/dist/codex-watch.js +100 -0
- package/dist/decisions.js +3 -3
- package/dist/delivery-deferral.d.ts +1 -19
- package/dist/delivery-deferral.js +1 -29
- package/dist/delivery-frame.d.ts +7 -0
- package/dist/delivery-frame.js +11 -0
- package/dist/delivery-ledger-sqlite.d.ts +97 -0
- package/dist/delivery-ledger-sqlite.js +496 -0
- package/dist/delivery-ledger.d.ts +196 -0
- package/dist/delivery-ledger.js +268 -0
- package/dist/delivery-lifetime.d.ts +6 -0
- package/dist/delivery-lifetime.js +45 -0
- package/dist/harness-links.js +61 -7
- package/dist/harness.js +12 -17
- package/dist/help.js +3 -6
- package/dist/http-delivery.d.ts +5 -9
- package/dist/http-delivery.js +20 -79
- package/dist/inbox.d.ts +6 -57
- package/dist/inbox.js +24 -380
- package/dist/index.d.ts +7 -4
- package/dist/index.js +5 -2
- 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 +15 -91
- package/dist/model.js +2 -140
- package/dist/neutral.d.ts +72 -0
- package/dist/neutral.js +27 -0
- package/dist/notifications.d.ts +24 -26
- package/dist/notifications.js +39 -143
- package/dist/operation-control.d.ts +4 -0
- package/dist/operation-control.js +20 -0
- 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/packages/agent-delivery/src/claude-native.d.ts +2 -0
- package/dist/packages/agent-delivery/src/claude-native.js +5 -0
- package/dist/packages/agent-delivery/src/codex-boundary.d.ts +11 -6
- package/dist/packages/agent-delivery/src/codex-boundary.js +31 -40
- package/dist/packages/agent-delivery/src/codex-hook.d.ts +17 -12
- package/dist/packages/agent-delivery/src/codex-hook.js +26 -39
- package/dist/packages/agent-delivery/src/codex.d.ts +3 -0
- package/dist/packages/agent-delivery/src/codex.js +41 -4
- package/dist/packages/agent-delivery/src/index.d.ts +3 -1
- package/dist/packages/agent-delivery/src/index.js +1 -1
- package/dist/packages/agent-delivery/src/paseo.js +11 -2
- package/dist/packages/agent-delivery/src/types.d.ts +1 -0
- package/dist/paseo-delivery.d.ts +5 -5
- package/dist/paseo-delivery.js +19 -99
- package/dist/paseo.d.ts +1 -1
- package/dist/paseo.js +1 -1
- package/dist/plugins/opencode/index.d.ts +5 -3
- package/dist/plugins/opencode/index.js +99 -21
- package/dist/plugins/opencode-shared/delivery.d.ts +23 -0
- package/dist/plugins/opencode-shared/delivery.js +109 -0
- package/dist/plugins/opencode-v1/index.d.ts +28 -0
- package/dist/plugins/opencode-v1/index.js +41 -0
- package/dist/plugins/opencode-v1/runtime.d.ts +56 -0
- package/dist/plugins/opencode-v1/runtime.js +152 -0
- package/dist/plugins/opencode-v1/transport.d.ts +13 -0
- package/dist/plugins/opencode-v1/transport.js +50 -0
- package/dist/plugins/paseo/shared/card-presentation.d.ts +10 -0
- package/dist/plugins/paseo/shared/card-presentation.js +7 -0
- package/dist/ports.d.ts +33 -214
- 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/session-lifecycle.d.ts +8 -7
- package/dist/session-lifecycle.js +40 -42
- package/dist/sqlite-framing.d.ts +8 -0
- package/dist/sqlite-framing.js +31 -0
- package/dist/sqlite-square.d.ts +16 -26
- package/dist/sqlite-square.js +199 -262
- 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 +5 -8
- package/dist/square-core.d.ts +0 -1
- package/dist/square-domain.d.ts +12 -10
- package/dist/square-domain.js +105 -223
- package/dist/square-facade.d.ts +25 -76
- package/dist/square-location.d.ts +6 -1
- package/dist/square-location.js +1 -1
- package/dist/square-post.d.ts +53 -0
- package/dist/square-post.js +4 -0
- package/dist/square-posting.d.ts +13 -0
- package/dist/square-posting.js +174 -0
- package/dist/square-producer-sqlite.d.ts +11 -0
- package/dist/square-producer-sqlite.js +210 -0
- package/dist/square-receive.d.ts +2 -3
- package/dist/square-sqlite.d.ts +33 -42
- package/dist/square-sqlite.js +236 -768
- package/dist/square-tools.d.ts +8 -0
- package/dist/square-tools.js +39 -0
- package/dist/square-values.d.ts +27 -0
- package/dist/square-values.js +90 -0
- package/dist/views.d.ts +6 -6
- package/dist/views.js +14 -9
- package/dist/wake-sink.d.ts +4 -11
- package/dist/wake-sink.js +8 -32
- package/dist/wake-transports.d.ts +4 -0
- package/dist/wake-transports.js +11 -0
- package/examples/symmetric-domains.mjs +29 -249
- package/guides/change-waits.md +13 -53
- package/guides/claude-native.md +19 -172
- package/guides/codex-delivery.md +55 -78
- package/guides/delivery-primitives.md +48 -207
- package/guides/operation-cancellation.md +2 -5
- package/guides/participant-ownership.md +25 -89
- package/guides/pi-native-delivery.md +19 -56
- package/guides/pi-tools.md +6 -8
- package/guides/row-runtime-delivery-evidence.md +2 -0
- package/guides/row-storage-design.md +19 -44
- package/guides/square-api.md +113 -538
- package/package.json +8 -8
- package/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/plugins/claude/hooks/hooks.json +1 -1
- package/plugins/claude/{hooks/register.js → mods/square.js} +4 -7
- package/plugins/claude/types.d.ts +1 -1
- package/plugins/codex/.codex-plugin/plugin.json +1 -1
- package/plugins/codex/hooks/hooks.json +11 -23
- package/plugins/opencode/index.ts +91 -23
- package/plugins/opencode-shared/delivery.ts +91 -0
- package/plugins/opencode-v1/index.ts +36 -0
- package/plugins/opencode-v1/runtime.ts +134 -0
- package/plugins/opencode-v1/transport.ts +41 -0
- package/plugins/paseo/OVERVIEW.md +5 -5
- package/plugins/paseo/README.md +7 -6
- package/plugins/paseo/client/markdown.tsx +5 -5
- package/plugins/paseo/client/square-message.tsx +28 -24
- package/plugins/paseo/client/transform.ts +8 -5
- package/plugins/paseo/shared/card-presentation.ts +16 -0
- package/plugins/paseo/shared/message.ts +6 -11
- package/plugins/pi/square-pi.js +167 -451
- package/dist/channel.d.ts +0 -74
- package/dist/channel.js +0 -92
- package/dist/claude-delivery-status.d.ts +0 -13
- package/dist/claude-delivery-status.js +0 -46
- package/dist/cmd/notify-once.d.ts +0 -2
- package/dist/cmd/notify-once.js +0 -29
- package/dist/delivery-attempts.d.ts +0 -52
- package/dist/delivery-attempts.js +0 -73
- package/dist/delivery-effects.d.ts +0 -139
- package/dist/delivery-effects.js +0 -163
- package/dist/delivery-health.d.ts +0 -51
- package/dist/delivery-health.js +0 -173
- package/dist/delivery-operations.d.ts +0 -9
- package/dist/delivery-operations.js +0 -168
- package/dist/delivery-primitives.d.ts +0 -5
- package/dist/delivery-primitives.js +0 -3
- package/dist/delivery.d.ts +0 -45
- package/dist/delivery.js +0 -106
- package/dist/plugins/opencode/delivery.d.ts +0 -19
- package/dist/plugins/opencode/delivery.js +0 -238
- package/dist/presentation-operations.d.ts +0 -18
- package/dist/presentation-operations.js +0 -86
- package/dist/square-index.d.ts +0 -16
- package/dist/square-index.js +0 -106
- 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/plugins/opencode/delivery.ts +0 -211
package/dist/delivery.js
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import { renderAttentionPreview, renderSquareReminder } from './attention-presentation.js';
|
|
2
|
-
import { parseActivityId } from './square-core.js';
|
|
3
|
-
import { deliveryDeferralExpiry } from './delivery-deferral.js';
|
|
4
|
-
import { automaticDispatchEligible, presentationBlocksDispatch } from './delivery-attempts.js';
|
|
5
|
-
import { matchesCatchSelection } from './catch-selection.js';
|
|
6
|
-
/** Exact stored activity projection for transports that send an awareness reminder. */
|
|
7
|
-
export function wakePreview(facts, request) {
|
|
8
|
-
const index = parseActivityId(request.activity);
|
|
9
|
-
const head = index === undefined ? undefined : pendingHead(facts, request.participant, index);
|
|
10
|
-
const item = index === undefined ? undefined : facts.activity(index);
|
|
11
|
-
const seat = facts.seat(request.participant);
|
|
12
|
-
if (head === undefined || item?.kind !== 'say' || seat === undefined || seat.attachment !== 'attached'
|
|
13
|
-
|| deliveryDeferralExpiry(facts, request.seat, item.index, Date.now()) !== undefined)
|
|
14
|
-
return undefined;
|
|
15
|
-
return renderSquareReminder([renderAttentionPreview({
|
|
16
|
-
squarePath: request.location, recipient: request.participant, actIndex: item.index,
|
|
17
|
-
actor: item.actor, route: head.route, body: item.body ?? '(structured activity)', delivery: item.delivery,
|
|
18
|
-
})], [item.delivery]);
|
|
19
|
-
}
|
|
20
|
-
function custody(evidence) {
|
|
21
|
-
return evidence.map(row => ({ purpose: row.kind === 'wake' ? 'dispatch' : 'presentation', outcome: row.outcome, expiresAt: row.expiresAt }));
|
|
22
|
-
}
|
|
23
|
-
/** Presentation custody blocks a wake, independently of native wake acceptance. */
|
|
24
|
-
export function presentationBlocksWake(evidence, now) {
|
|
25
|
-
return presentationBlocksDispatch(custody(evidence), now);
|
|
26
|
-
}
|
|
27
|
-
/** Preselection only: an expired wake dispatch is uncertain, never safe automatic replay. */
|
|
28
|
-
export function automaticWakeEligible(evidence, now) {
|
|
29
|
-
return automaticDispatchEligible(custody(evidence), now);
|
|
30
|
-
}
|
|
31
|
-
/** Notification pending is not cursor suffix or historical catch membership. */
|
|
32
|
-
export function pendingHead(facts, name, index) {
|
|
33
|
-
const participant = facts.participant(name);
|
|
34
|
-
if (!participant?.joined || index <= (participant.lastJoinIndex ?? -1) || facts.receipt(participant.name, index))
|
|
35
|
-
return undefined;
|
|
36
|
-
return facts.directedAt(participant.name, index);
|
|
37
|
-
}
|
|
38
|
-
/** One activity's landed recipients that still owe a wake; the rest of the roster is untouched. */
|
|
39
|
-
export function pendingRecipients(facts, index) {
|
|
40
|
-
return facts.audience(index).flatMap(({ recipient }) => {
|
|
41
|
-
const head = pendingHead(facts, recipient, index);
|
|
42
|
-
return head === undefined ? [] : [head];
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
/** Still-pending directed heads for one recipient, oldest first. Bodies belong to presentation. */
|
|
46
|
-
export function pendingHeads(facts, name) {
|
|
47
|
-
const participant = facts.participant(name);
|
|
48
|
-
if (!participant?.joined)
|
|
49
|
-
return [];
|
|
50
|
-
const heads = [];
|
|
51
|
-
let afterIndex = participant.lastJoinIndex ?? -1;
|
|
52
|
-
for (;;) {
|
|
53
|
-
const page = facts.directed(participant.name, { afterIndex, unseenOnly: true }, { edge: 'oldest', limit: 100 });
|
|
54
|
-
heads.push(...page.items);
|
|
55
|
-
if (!page.hasMore)
|
|
56
|
-
break;
|
|
57
|
-
afterIndex = page.items.at(-1).index;
|
|
58
|
-
}
|
|
59
|
-
return heads;
|
|
60
|
-
}
|
|
61
|
-
export function capturePendingScan(facts) {
|
|
62
|
-
return { roster: facts.roster().filter((participant) => participant.joined), throughIndex: facts.header().lastActIndex };
|
|
63
|
-
}
|
|
64
|
-
export function sameJoin(facts, captured) {
|
|
65
|
-
const current = facts.participant(captured.name);
|
|
66
|
-
return current?.joined === captured.joined && current?.lastJoinIndex === captured.lastJoinIndex;
|
|
67
|
-
}
|
|
68
|
-
/** Finite bodyless pass: unrelated revisions never rewind the last examined group. */
|
|
69
|
-
export async function* pendingPages(artifact, scan, afterIndex = -1) {
|
|
70
|
-
const captured = new Map(scan.roster.map((participant) => [participant.name, participant]));
|
|
71
|
-
while (afterIndex < scan.throughIndex) {
|
|
72
|
-
const { value: page } = await artifact.read((facts) => {
|
|
73
|
-
const page = facts.pendingAudience(afterIndex, scan.throughIndex, 100);
|
|
74
|
-
return { ...page, groups: page.groups.map((group) => ({ ...group, recipients: group.recipients.filter((recipient) => {
|
|
75
|
-
const participant = captured.get(recipient.recipient);
|
|
76
|
-
return participant !== undefined && sameJoin(facts, participant);
|
|
77
|
-
}) })) };
|
|
78
|
-
});
|
|
79
|
-
yield page.groups.flatMap((group) => group.recipients.map(({ recipient, route }) => ({
|
|
80
|
-
index: group.index, at: group.at, actor: group.actor, recipient, route,
|
|
81
|
-
})));
|
|
82
|
-
if (!page.hasMore)
|
|
83
|
-
break;
|
|
84
|
-
afterIndex = page.groups.at(-1).index;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
/** A pass retains only its captured joins; routes and individual receipts are fresh. */
|
|
88
|
-
export function pendingFacts(facts, scan, heads) {
|
|
89
|
-
const joined = new Set(scan.roster.filter((participant) => sameJoin(facts, participant)).map((participant) => participant.name));
|
|
90
|
-
return { pending: heads.filter((head) => joined.has(head.recipient)
|
|
91
|
-
&& pendingHead(facts, head.recipient, head.index) !== undefined) };
|
|
92
|
-
}
|
|
93
|
-
/** Global discovery alone sweeps pendingAudience; exact dispatch uses pendingRecipients. */
|
|
94
|
-
export async function collectPending(artifact) {
|
|
95
|
-
const { value: scan } = await artifact.read(capturePendingScan);
|
|
96
|
-
const pending = [];
|
|
97
|
-
for await (const page of pendingPages(artifact, scan))
|
|
98
|
-
pending.push(...page);
|
|
99
|
-
const { value } = await artifact.read((facts) => ({ scan, ...pendingFacts(facts, scan, pending) }));
|
|
100
|
-
return value;
|
|
101
|
-
}
|
|
102
|
-
/** A live catch owns every matching notification, independent of its page size. */
|
|
103
|
-
export function leaseOwnsNotification(lease, notification) {
|
|
104
|
-
return matchesCatchSelection(notification.actor, notification.route === 'bell' ? { kind: 'bell' }
|
|
105
|
-
: { kind: 'mentions', names: notification.route === 'mention' ? [notification.recipient] : [] }, lease.filter ?? {}, notification.route === 'reply' ? notification.recipient : undefined);
|
|
106
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { type OpenCodeNativeSession } from '../../packages/agent-delivery/src/opencode-native.js';
|
|
2
|
-
export interface OpenCodeContextMessage {
|
|
3
|
-
readonly id?: string;
|
|
4
|
-
readonly role: string;
|
|
5
|
-
readonly content: readonly {
|
|
6
|
-
readonly type: string;
|
|
7
|
-
readonly text?: string;
|
|
8
|
-
}[];
|
|
9
|
-
}
|
|
10
|
-
export declare function indexedOpenCodeSessions(env: NodeJS.ProcessEnv): Promise<readonly string[]>;
|
|
11
|
-
export declare function discoverOpenCodeSeats(sessionId: string, env: NodeJS.ProcessEnv, signal?: AbortSignal): Promise<import("../../model.js").LocatedSeat[]>;
|
|
12
|
-
/** The exact prepared native message can authorize artifact-backed presentation once. */
|
|
13
|
-
export declare function observeOpenCodeContext(sessionId: string, messages: readonly OpenCodeContextMessage[], env: NodeJS.ProcessEnv, signal: AbortSignal): Promise<void>;
|
|
14
|
-
/** The native receiver owns no claims outside the artifact; HOME is used only for discovery. */
|
|
15
|
-
export declare function receiveOpenCodePending(target: OpenCodeNativeSession, env: NodeJS.ProcessEnv, signal: AbortSignal): Promise<void>;
|
|
16
|
-
/** Publish the native address into current artifact seats; the legacy route projection is not read. */
|
|
17
|
-
export declare function publishOpenCodeAddress(sessionId: string, env: NodeJS.ProcessEnv, signal: AbortSignal): Promise<boolean>;
|
|
18
|
-
export declare function cancelOpenCodeDeliveries(sessionId: string, env: NodeJS.ProcessEnv, signal: AbortSignal): Promise<void>;
|
|
19
|
-
export declare function endOpenCodeSession(sessionId: string, env: NodeJS.ProcessEnv, signal: AbortSignal): Promise<void>;
|
|
@@ -1,238 +0,0 @@
|
|
|
1
|
-
import { createHash } from 'node:crypto';
|
|
2
|
-
import { createArtifactPort } from '../../artifact.js';
|
|
3
|
-
import { deliverPending } from '../../delivery-operations.js';
|
|
4
|
-
import { nativePendingPreview, observeSessionPending, projectSessionSeats } from '../../inbox.js';
|
|
5
|
-
import { automaticWakeEligible, presentationBlocksWake } from '../../delivery.js';
|
|
6
|
-
import { harnessOwner, decodeHarnessOwner } from '../../owner.js';
|
|
7
|
-
import { formatActivityId, parseActivityId } from '../../square-core.js';
|
|
8
|
-
import { indexedSquareEntries } from '../../square-index.js';
|
|
9
|
-
import { asSeatRef } from '../../seats.js';
|
|
10
|
-
import { restoreSessionSeats } from '../../session-lifecycle.js';
|
|
11
|
-
import { presentPending } from '../../presentation-operations.js';
|
|
12
|
-
import { createNativeInputId, sendNativeText } from '../../packages/agent-delivery/src/opencode-native.js';
|
|
13
|
-
const routeKind = 'opencode-server';
|
|
14
|
-
const ownerFor = (sessionId) => harnessOwner({ channel: 'opencode', session: sessionId });
|
|
15
|
-
const payloadHash = (payload) => createHash('sha256').update(payload).digest('hex');
|
|
16
|
-
function preview(facts, location, captured, index) {
|
|
17
|
-
const seat = facts.seat(captured.participant);
|
|
18
|
-
if (seat === undefined || seat.attachment !== 'attached' || seat.generation !== captured.generation || seat.owner !== captured.owner)
|
|
19
|
-
return undefined;
|
|
20
|
-
const wake = seat.wakeAddress;
|
|
21
|
-
if (wake?.kind !== routeKind)
|
|
22
|
-
return undefined;
|
|
23
|
-
return nativePendingPreview(facts, {
|
|
24
|
-
location, routeKind, generation: seat.generation, participant: seat.participant,
|
|
25
|
-
actIndex: index, address: wake.address, now: Date.now(),
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
export async function indexedOpenCodeSessions(env) {
|
|
29
|
-
return [...new Set((await indexedSquareEntries({}, env)).flatMap((entry) => {
|
|
30
|
-
const coordinate = decodeHarnessOwner(entry.owner);
|
|
31
|
-
return coordinate?.channel === 'opencode' ? [coordinate.session] : [];
|
|
32
|
-
}))];
|
|
33
|
-
}
|
|
34
|
-
export async function discoverOpenCodeSeats(sessionId, env, signal) {
|
|
35
|
-
return projectSessionSeats({ owner: ownerFor(sessionId), env }, signal);
|
|
36
|
-
}
|
|
37
|
-
function sameSeat(left, right) {
|
|
38
|
-
return left !== undefined && left.attachment === 'attached' && left.generation === right.generation && left.owner === right.owner;
|
|
39
|
-
}
|
|
40
|
-
function createOpenCodeWakeTransport(target, lifetime) {
|
|
41
|
-
return {
|
|
42
|
-
probe: async (route) => !lifetime.aborted && route.kind === routeKind
|
|
43
|
-
&& route.address.sessionId === target.sessionId,
|
|
44
|
-
async attempt(request, timeoutMs, beforeSend) {
|
|
45
|
-
const index = parseActivityId(request.activity);
|
|
46
|
-
const artifact = request.artifact;
|
|
47
|
-
if (artifact === undefined || request.claimToken === undefined || index === undefined
|
|
48
|
-
|| request.route.kind !== routeKind || request.route.address.sessionId !== target.sessionId) {
|
|
49
|
-
return { outcome: 'not-capable', diagnostic: 'OpenCode attention has no matching artifact claim or session.' };
|
|
50
|
-
}
|
|
51
|
-
const signal = AbortSignal.any([lifetime, AbortSignal.timeout(timeoutMs)]);
|
|
52
|
-
const started = Date.now();
|
|
53
|
-
if (signal.aborted || !await beforeSend())
|
|
54
|
-
return { outcome: 'not-capable', diagnostic: 'OpenCode attention changed before send.' };
|
|
55
|
-
const { value: prepared } = await artifact.read((facts) => preview(facts, request.location, request.seat, index), signal);
|
|
56
|
-
if (prepared === undefined)
|
|
57
|
-
return { outcome: 'not-capable', diagnostic: 'OpenCode attention is no longer current.' };
|
|
58
|
-
const inputId = createNativeInputId();
|
|
59
|
-
const correlation = { harness: 'opencode', token: inputId, payloadHash: payloadHash(prepared.payload) };
|
|
60
|
-
if (!await artifact.prepareDelivery({ seat: request.seat, claimToken: request.claimToken, correlation }, signal))
|
|
61
|
-
return { outcome: 'not-capable', diagnostic: 'OpenCode delivery claim is no longer current.' };
|
|
62
|
-
const fresh = await artifact.read((facts) => preview(facts, request.location, request.seat, index), signal);
|
|
63
|
-
if (fresh.value === undefined || payloadHash(fresh.value.payload) !== correlation.payloadHash)
|
|
64
|
-
return { outcome: 'not-capable', diagnostic: 'OpenCode attention changed before native send.' };
|
|
65
|
-
if (signal.aborted || !await beforeSend())
|
|
66
|
-
return { outcome: 'not-capable', diagnostic: 'OpenCode attention was consumed before send.' };
|
|
67
|
-
const result = await sendNativeText(target, prepared.payload, {
|
|
68
|
-
inputId, delivery: 'steer', timeoutMs: Math.max(1, timeoutMs - (Date.now() - started)), signal,
|
|
69
|
-
});
|
|
70
|
-
if (result.state === 'accepted')
|
|
71
|
-
return { outcome: 'accepted' };
|
|
72
|
-
if (result.state === 'unavailable')
|
|
73
|
-
return { outcome: 'failed', signature: result.code };
|
|
74
|
-
return { outcome: 'unknown', signature: result.code };
|
|
75
|
-
},
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
function textMessage(message) {
|
|
79
|
-
if (message.role !== 'user' || message.id === undefined || !message.content.every((part) => part.type === 'text' && typeof part.text === 'string'))
|
|
80
|
-
return undefined;
|
|
81
|
-
return message.content.map((part) => part.text).join('');
|
|
82
|
-
}
|
|
83
|
-
/** The exact prepared native message can authorize artifact-backed presentation once. */
|
|
84
|
-
export async function observeOpenCodeContext(sessionId, messages, env, signal) {
|
|
85
|
-
const observed = new Map(messages.flatMap((message) => {
|
|
86
|
-
const text = textMessage(message);
|
|
87
|
-
return text === undefined ? [] : [[message.id, payloadHash(text)]];
|
|
88
|
-
}));
|
|
89
|
-
if (observed.size === 0 || signal.aborted)
|
|
90
|
-
return;
|
|
91
|
-
for (const { location, seat } of await discoverOpenCodeSeats(sessionId, env, signal)) {
|
|
92
|
-
if (signal.aborted)
|
|
93
|
-
return;
|
|
94
|
-
const artifact = createArtifactPort(location, undefined, signal);
|
|
95
|
-
try {
|
|
96
|
-
const { value: matches } = await artifact.read((facts) => {
|
|
97
|
-
if (!sameSeat(facts.seat(seat.participant), seat))
|
|
98
|
-
return [];
|
|
99
|
-
return facts.deliveryEvidence(seat.participant, seat.generation).flatMap((evidence) => {
|
|
100
|
-
const token = evidence.correlation?.harness === 'opencode' ? evidence.correlation.token : undefined;
|
|
101
|
-
const hash = token === undefined ? undefined : observed.get(token);
|
|
102
|
-
const index = parseActivityId(evidence.activity);
|
|
103
|
-
if (token === undefined || hash === undefined || evidence.claimToken === undefined || index === undefined
|
|
104
|
-
|| !['dispatching', 'unknown', 'accepted'].includes(evidence.outcome)
|
|
105
|
-
|| evidence.correlation?.payloadHash !== hash)
|
|
106
|
-
return [];
|
|
107
|
-
const prepared = preview(facts, location, seat, index);
|
|
108
|
-
if (prepared === undefined || payloadHash(prepared.payload) !== hash
|
|
109
|
-
|| presentationBlocksWake(facts.deliveryEvidence(seat.participant, seat.generation, evidence.activity), Date.now()))
|
|
110
|
-
return [];
|
|
111
|
-
return [{ evidence, index, hash, clipped: prepared.clipped }];
|
|
112
|
-
});
|
|
113
|
-
}, signal);
|
|
114
|
-
for (const { evidence, index, hash, clipped } of matches) {
|
|
115
|
-
if (signal.aborted || evidence.claimToken === undefined)
|
|
116
|
-
return;
|
|
117
|
-
await presentPending({ artifact, location, participant: seat.participant,
|
|
118
|
-
activity: formatActivityId(index), seat: asSeatRef(seat), observationOf: evidence.claimToken, sink: { present() { } }, markSeen: !clipped, signal,
|
|
119
|
-
current: (facts) => {
|
|
120
|
-
const current = facts.seat(seat.participant);
|
|
121
|
-
const prepared = current === undefined || !sameSeat(current, seat) ? undefined : preview(facts, location, seat, index);
|
|
122
|
-
const correlation = facts.deliveryByToken(evidence.claimToken);
|
|
123
|
-
return prepared !== undefined && payloadHash(prepared.payload) === hash
|
|
124
|
-
&& correlation?.correlation?.harness === 'opencode'
|
|
125
|
-
&& correlation.correlation.token !== undefined
|
|
126
|
-
&& correlation.correlation.payloadHash === hash;
|
|
127
|
-
},
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
finally {
|
|
132
|
-
await artifact.close();
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
/** The native receiver owns no claims outside the artifact; HOME is used only for discovery. */
|
|
137
|
-
export async function receiveOpenCodePending(target, env, signal) {
|
|
138
|
-
const observer = await observeSessionPending(ownerFor(target.sessionId), env);
|
|
139
|
-
const transport = createOpenCodeWakeTransport(target, signal);
|
|
140
|
-
let waiting;
|
|
141
|
-
try {
|
|
142
|
-
while (!signal.aborted) {
|
|
143
|
-
const armed = new Promise((resolve) => {
|
|
144
|
-
waiting = observer.wait(Infinity, { signal, skipImmediate: true, onChangeArmed: resolve });
|
|
145
|
-
void waiting.catch(() => resolve(false));
|
|
146
|
-
});
|
|
147
|
-
if (!await armed) {
|
|
148
|
-
await waiting;
|
|
149
|
-
break;
|
|
150
|
-
}
|
|
151
|
-
for (const membership of await observer.read({ signal })) {
|
|
152
|
-
const { location, seat } = membership;
|
|
153
|
-
if (signal.aborted)
|
|
154
|
-
return;
|
|
155
|
-
const artifact = createArtifactPort(location, undefined, signal);
|
|
156
|
-
try {
|
|
157
|
-
const { value: notifications } = await artifact.read((facts) => {
|
|
158
|
-
if (!sameSeat(facts.seat(seat.participant), seat))
|
|
159
|
-
return [];
|
|
160
|
-
return membership.notifications.filter((note) => automaticWakeEligible(facts.deliveryEvidence(seat.participant, seat.generation, formatActivityId(note.actIndex)), Date.now()));
|
|
161
|
-
}, signal);
|
|
162
|
-
for (const note of notifications) {
|
|
163
|
-
if (signal.aborted)
|
|
164
|
-
return;
|
|
165
|
-
await deliverPending({ artifact, transport, location, activity: formatActivityId(note.actIndex), now: Date.now() });
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
catch (error) {
|
|
169
|
-
if (signal.aborted)
|
|
170
|
-
throw signal.reason ?? error;
|
|
171
|
-
// A candidate that became unreadable cannot authorize sending; the shared observer
|
|
172
|
-
// keeps its subscription and will reconsider the artifact after repair.
|
|
173
|
-
}
|
|
174
|
-
finally {
|
|
175
|
-
await artifact.close();
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
await waiting;
|
|
179
|
-
waiting = undefined;
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
catch (error) {
|
|
183
|
-
if (!signal.aborted)
|
|
184
|
-
throw error;
|
|
185
|
-
}
|
|
186
|
-
finally {
|
|
187
|
-
observer.close();
|
|
188
|
-
await waiting?.catch(() => { });
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
/** Publish the native address into current artifact seats; the legacy route projection is not read. */
|
|
192
|
-
export async function publishOpenCodeAddress(sessionId, env, signal) {
|
|
193
|
-
await restoreSessionSeats(ownerFor(sessionId), env, { kind: routeKind, address: { sessionId } }, signal);
|
|
194
|
-
const seats = (await discoverOpenCodeSeats(sessionId, env, signal)).filter(({ seat }) => seat.attachment === 'attached');
|
|
195
|
-
for (const { location, seat } of seats) {
|
|
196
|
-
if (signal.aborted)
|
|
197
|
-
return false;
|
|
198
|
-
const artifact = createArtifactPort(location, undefined, signal);
|
|
199
|
-
try {
|
|
200
|
-
await artifact.transactAs({ kind: 'participant', seat: asSeatRef(seat) }, (facts) => facts.updateWakeAddress(asSeatRef(seat), { kind: routeKind, address: { sessionId } }));
|
|
201
|
-
}
|
|
202
|
-
finally {
|
|
203
|
-
await artifact.close();
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
return seats.length > 0;
|
|
207
|
-
}
|
|
208
|
-
export async function cancelOpenCodeDeliveries(sessionId, env, signal) {
|
|
209
|
-
for (const { location, seat } of await discoverOpenCodeSeats(sessionId, env, signal)) {
|
|
210
|
-
if (signal.aborted)
|
|
211
|
-
return;
|
|
212
|
-
if (seat.attachment !== 'attached')
|
|
213
|
-
continue;
|
|
214
|
-
const artifact = createArtifactPort(location, undefined, signal);
|
|
215
|
-
try {
|
|
216
|
-
const { value: through } = await artifact.read((facts) => facts.header().lastActIndex, signal);
|
|
217
|
-
if (through >= 0)
|
|
218
|
-
await artifact.cancelDeliveries(asSeatRef(seat), through, signal);
|
|
219
|
-
}
|
|
220
|
-
finally {
|
|
221
|
-
await artifact.close();
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
export async function endOpenCodeSession(sessionId, env, signal) {
|
|
226
|
-
for (const { location, seat } of await discoverOpenCodeSeats(sessionId, env, signal)) {
|
|
227
|
-
if (signal.aborted)
|
|
228
|
-
return;
|
|
229
|
-
const { Square } = await import('../../square-api.js');
|
|
230
|
-
const square = await Square.at({ path: location, env });
|
|
231
|
-
try {
|
|
232
|
-
await square.endHeld(seat.participant, { owner: seat.owner, generation: seat.generation });
|
|
233
|
-
}
|
|
234
|
-
finally {
|
|
235
|
-
await square.close();
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import type { PresentationResult, PresentPendingInput } from './ports.js';
|
|
2
|
-
/** Prepare custody before any external effect. A batch prepares every included item first. */
|
|
3
|
-
export declare function preparePendingPresentation(input: Omit<PresentPendingInput, 'sink'>): Promise<import("./delivery-effects.js").PreparedPresentation<{
|
|
4
|
-
kind: "say";
|
|
5
|
-
actor: import("./square-core.js").Participant;
|
|
6
|
-
at?: number;
|
|
7
|
-
body?: string;
|
|
8
|
-
metadata?: import("./model.js").ActivityMetadata;
|
|
9
|
-
delivery?: import("./model.js").ActivityDelivery;
|
|
10
|
-
mentions?: readonly import("./square-core.js").Participant[];
|
|
11
|
-
reach?: import("./square-core.js").Reach;
|
|
12
|
-
reply?: number;
|
|
13
|
-
} & {
|
|
14
|
-
index: number;
|
|
15
|
-
at: number;
|
|
16
|
-
}> | undefined>;
|
|
17
|
-
/** A sink error cannot prove that content was never injected. */
|
|
18
|
-
export declare function presentPending(input: PresentPendingInput): Promise<PresentationResult>;
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import { randomUUID } from 'node:crypto';
|
|
2
|
-
import { formatActivityId, parseActivityId } from './square-core.js';
|
|
3
|
-
import { isSquareError } from './model.js';
|
|
4
|
-
import { pendingHead, sameJoin } from './delivery.js';
|
|
5
|
-
import { deliveryDeferralExpiry } from './delivery-deferral.js';
|
|
6
|
-
import { asSeatRef } from './seats.js';
|
|
7
|
-
import { preparePresentation, presentPrepared } from './delivery-effects.js';
|
|
8
|
-
/** Prepare custody before any external effect. A batch prepares every included item first. */
|
|
9
|
-
export async function preparePendingPresentation(input) {
|
|
10
|
-
const index = typeof input.activity === 'number' ? input.activity : parseActivityId(input.activity);
|
|
11
|
-
if (index === undefined)
|
|
12
|
-
return undefined;
|
|
13
|
-
const initial = await input.artifact.read((facts) => {
|
|
14
|
-
const participant = facts.participant(input.participant);
|
|
15
|
-
const seat = facts.seat(input.participant);
|
|
16
|
-
if (!participant?.joined || seat === undefined || seat.attachment !== 'attached' || input.generation !== undefined && input.generation !== seat.generation
|
|
17
|
-
|| input.seat !== undefined && (input.seat.generation !== seat.generation || input.seat.owner !== seat.owner)
|
|
18
|
-
|| input.current?.(facts) === false || !pendingHead(facts, participant.name, index)
|
|
19
|
-
|| deliveryDeferralExpiry(facts, seat, index, input.now ?? Date.now()) !== undefined)
|
|
20
|
-
return undefined;
|
|
21
|
-
const item = facts.activity(index);
|
|
22
|
-
return item?.kind === 'say' ? { participant, seat, item } : undefined;
|
|
23
|
-
}, input.signal).catch(() => undefined);
|
|
24
|
-
if (initial?.value === undefined)
|
|
25
|
-
return undefined;
|
|
26
|
-
const { participant, seat, item } = initial.value;
|
|
27
|
-
const ref = asSeatRef(seat);
|
|
28
|
-
const owns = (facts) => {
|
|
29
|
-
const current = facts.seat(seat.participant);
|
|
30
|
-
return current !== undefined && current.attachment === 'attached' && current.owner === seat.owner && current.generation === seat.generation
|
|
31
|
-
&& sameJoin(facts, participant);
|
|
32
|
-
};
|
|
33
|
-
const claimToken = randomUUID();
|
|
34
|
-
return preparePresentation({
|
|
35
|
-
payload: item, coverage: input.markSeen === false ? 'clipped' : 'full',
|
|
36
|
-
port: {
|
|
37
|
-
async claim(signal) {
|
|
38
|
-
const claimed = await input.artifact.claimDelivery({ seat: ref, activity: formatActivityId(index), kind: 'presentation',
|
|
39
|
-
observationOf: input.observationOf, at: input.now ?? Date.now(), leaseMs: input.timeoutMs ?? 5000, claimToken }, signal).catch(() => ({ status: 'stale' }));
|
|
40
|
-
return claimed.status === 'acquired' ? { token: claimToken, ordinal: claimed.attemptN } : undefined;
|
|
41
|
-
},
|
|
42
|
-
async current(signal) {
|
|
43
|
-
return (await input.artifact.read(facts => owns(facts) && input.current?.(facts) !== false
|
|
44
|
-
&& pendingHead(facts, participant.name, index) !== undefined
|
|
45
|
-
&& deliveryDeferralExpiry(facts, seat, index, input.now ?? Date.now()) === undefined, signal ?? input.signal)).value;
|
|
46
|
-
},
|
|
47
|
-
async settle(claim, completion) {
|
|
48
|
-
const success = completion.outcome === 'presented' || completion.outcome === 'clipped';
|
|
49
|
-
const { value } = await input.artifact.settleAs({ kind: 'participant', seat: ref }, facts => {
|
|
50
|
-
// Captured ownership fences truth; post-effect policy/cancellation cannot veto it.
|
|
51
|
-
if (!owns(facts))
|
|
52
|
-
return false;
|
|
53
|
-
if (!facts.completeDelivery({ seat: ref, activity: formatActivityId(index), claimToken: claim.token,
|
|
54
|
-
outcome: completion.outcome, at: success ? input.now ?? Date.now() : Date.now(),
|
|
55
|
-
...(completion.reason === 'threw' ? { message: completion.error instanceof Error ? completion.error.message : String(completion.error) } : {}),
|
|
56
|
-
}))
|
|
57
|
-
return false;
|
|
58
|
-
if (completion.outcome === 'presented')
|
|
59
|
-
facts.markSeen(participant.name, [index], input.now ?? Date.now());
|
|
60
|
-
return true;
|
|
61
|
-
}).catch(error => {
|
|
62
|
-
if (!success || isSquareError(error) && error.code === 'already_joined')
|
|
63
|
-
return { value: false };
|
|
64
|
-
throw error;
|
|
65
|
-
});
|
|
66
|
-
return value;
|
|
67
|
-
},
|
|
68
|
-
},
|
|
69
|
-
}, { signal: input.signal });
|
|
70
|
-
}
|
|
71
|
-
/** A sink error cannot prove that content was never injected. */
|
|
72
|
-
export async function presentPending(input) {
|
|
73
|
-
const prepared = await preparePendingPresentation(input);
|
|
74
|
-
if (prepared === undefined)
|
|
75
|
-
return { presented: false };
|
|
76
|
-
const report = await presentPrepared(prepared, activity => input.sink.present(activity), {
|
|
77
|
-
signal: input.signal,
|
|
78
|
-
});
|
|
79
|
-
if (report.observation.kind === 'threw')
|
|
80
|
-
throw report.observation.error;
|
|
81
|
-
const result = report.settlements[0].result;
|
|
82
|
-
if (result.persistence.status === 'error')
|
|
83
|
-
throw result.persistence.error;
|
|
84
|
-
return report.observation.kind === 'returned' && result.persistence.status === 'recorded'
|
|
85
|
-
? { presented: true, activity: prepared.payload } : { presented: false };
|
|
86
|
-
}
|
package/dist/square-index.d.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { type VersionObserver } from './file-changes.js';
|
|
2
|
-
import { type Owner } from './model.js';
|
|
3
|
-
/** A rebuildable discovery hint: the owner that may have used an artifact and its path. */
|
|
4
|
-
export interface SquareIndexEntry {
|
|
5
|
-
readonly location: string;
|
|
6
|
-
readonly owner: Owner;
|
|
7
|
-
}
|
|
8
|
-
export interface SquareIndexQuery {
|
|
9
|
-
readonly owner?: Owner;
|
|
10
|
-
}
|
|
11
|
-
export declare function squareIndexPath(env?: NodeJS.ProcessEnv): string;
|
|
12
|
-
export declare function indexedSquareEntries(query?: SquareIndexQuery, env?: NodeJS.ProcessEnv): Promise<readonly SquareIndexEntry[]>;
|
|
13
|
-
export declare function indexedSquarePaths(query?: SquareIndexQuery, env?: NodeJS.ProcessEnv): Promise<readonly string[]>;
|
|
14
|
-
/** Best-effort discovery only: a busy index skips publication, never queues or delays artifact work. */
|
|
15
|
-
export declare function rememberSquarePath(location: string, owner: Owner, index: string): Promise<boolean>;
|
|
16
|
-
export declare function observeSquareIndex(env?: NodeJS.ProcessEnv): Promise<VersionObserver<string>>;
|
package/dist/square-index.js
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import { promises as fs } from 'node:fs';
|
|
2
|
-
import os from 'node:os';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import { randomUUID } from 'node:crypto';
|
|
5
|
-
import { withFileLock } from './file-lock.js';
|
|
6
|
-
import { canonicalPath } from './canonical-path.js';
|
|
7
|
-
import { observeFileMetadata } from './file-changes.js';
|
|
8
|
-
import { validateOwner } from './model.js';
|
|
9
|
-
export function squareIndexPath(env = process.env) {
|
|
10
|
-
const configured = env.SQUARE_INDEX?.trim();
|
|
11
|
-
return path.resolve(configured ?? path.join(os.homedir(), '.square', 'index.ndjsonl'));
|
|
12
|
-
}
|
|
13
|
-
function lockPath(index) {
|
|
14
|
-
return `${index}.lock`;
|
|
15
|
-
}
|
|
16
|
-
function validEntry(value) {
|
|
17
|
-
if (value === null || typeof value !== 'object' || Array.isArray(value))
|
|
18
|
-
return undefined;
|
|
19
|
-
const row = value;
|
|
20
|
-
if (typeof row.location !== 'string' || !path.isAbsolute(row.location) || typeof row.owner !== 'string')
|
|
21
|
-
return undefined;
|
|
22
|
-
try {
|
|
23
|
-
validateOwner(row.owner);
|
|
24
|
-
}
|
|
25
|
-
catch {
|
|
26
|
-
return undefined;
|
|
27
|
-
}
|
|
28
|
-
if (Object.keys(row).some((key) => !['location', 'owner'].includes(key)))
|
|
29
|
-
return undefined;
|
|
30
|
-
return { location: row.location, owner: row.owner };
|
|
31
|
-
}
|
|
32
|
-
async function readEntries(index) {
|
|
33
|
-
let content;
|
|
34
|
-
try {
|
|
35
|
-
content = await fs.readFile(index, 'utf8');
|
|
36
|
-
}
|
|
37
|
-
catch (error) {
|
|
38
|
-
if (error.code === 'ENOENT')
|
|
39
|
-
return [];
|
|
40
|
-
throw error;
|
|
41
|
-
}
|
|
42
|
-
const entries = [];
|
|
43
|
-
for (const line of content.split('\n')) {
|
|
44
|
-
if (line.trim() === '')
|
|
45
|
-
continue;
|
|
46
|
-
try {
|
|
47
|
-
const entry = validEntry(JSON.parse(line));
|
|
48
|
-
if (entry !== undefined)
|
|
49
|
-
entries.push(entry);
|
|
50
|
-
}
|
|
51
|
-
catch { /* Disposable discovery input. */ }
|
|
52
|
-
}
|
|
53
|
-
return entries;
|
|
54
|
-
}
|
|
55
|
-
function matches(entry, query) {
|
|
56
|
-
return query.owner === undefined || entry.owner === query.owner;
|
|
57
|
-
}
|
|
58
|
-
export async function indexedSquareEntries(query = {}, env = process.env) {
|
|
59
|
-
const entries = await readEntries(squareIndexPath(env));
|
|
60
|
-
const result = [];
|
|
61
|
-
const seen = new Set();
|
|
62
|
-
for (const entry of entries) {
|
|
63
|
-
if (!matches(entry, query))
|
|
64
|
-
continue;
|
|
65
|
-
let location;
|
|
66
|
-
try {
|
|
67
|
-
location = await canonicalPath(entry.location);
|
|
68
|
-
}
|
|
69
|
-
catch {
|
|
70
|
-
continue;
|
|
71
|
-
}
|
|
72
|
-
const key = JSON.stringify([location, entry.owner]);
|
|
73
|
-
if (seen.has(key))
|
|
74
|
-
continue;
|
|
75
|
-
seen.add(key);
|
|
76
|
-
result.push({ location, owner: entry.owner });
|
|
77
|
-
}
|
|
78
|
-
return result;
|
|
79
|
-
}
|
|
80
|
-
export async function indexedSquarePaths(query = {}, env = process.env) {
|
|
81
|
-
return [...new Set((await indexedSquareEntries(query, env)).map((entry) => entry.location))];
|
|
82
|
-
}
|
|
83
|
-
/** Best-effort discovery only: a busy index skips publication, never queues or delays artifact work. */
|
|
84
|
-
export async function rememberSquarePath(location, owner, index) {
|
|
85
|
-
validateOwner(owner);
|
|
86
|
-
const canonical = await canonicalPath(location);
|
|
87
|
-
await fs.mkdir(path.dirname(index), { recursive: true });
|
|
88
|
-
return await withFileLock(lockPath(index), { retryMs: 10, skipIfBusy: true }, async () => {
|
|
89
|
-
const entries = await readEntries(index);
|
|
90
|
-
if (entries.some((entry) => entry.location === canonical && entry.owner === owner))
|
|
91
|
-
return false;
|
|
92
|
-
const next = [...entries, { location: canonical, owner }];
|
|
93
|
-
const temporary = `${index}.${process.pid}.${randomUUID()}.tmp`;
|
|
94
|
-
try {
|
|
95
|
-
await fs.writeFile(temporary, next.map((entry) => JSON.stringify(entry)).join('\n') + '\n', { flag: 'wx', mode: 0o600 });
|
|
96
|
-
await fs.rename(temporary, index);
|
|
97
|
-
}
|
|
98
|
-
finally {
|
|
99
|
-
await fs.rm(temporary, { force: true });
|
|
100
|
-
}
|
|
101
|
-
return true;
|
|
102
|
-
}) ?? false;
|
|
103
|
-
}
|
|
104
|
-
export function observeSquareIndex(env = process.env) {
|
|
105
|
-
return observeFileMetadata(squareIndexPath(env));
|
|
106
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import type { DatabaseSync } from 'node:sqlite';
|
|
2
|
-
export declare const FORMAT4_ADDED_OBJECTS: ReadonlySet<string>;
|
|
3
|
-
export declare const FORMAT4_REPLACED_OBJECTS: ReadonlySet<string>;
|
|
4
|
-
/** Historical activity framing only; no runtime activity decoder accepts these kinds. */
|
|
5
|
-
export declare function legacyActivitySchema(currentSchemaSql: string): string;
|
|
6
|
-
/**
|
|
7
|
-
* Read-only recognition of the exact row framing accepted by explicit migration.
|
|
8
|
-
* It inspects identity, owned schema and journal mode only; it never reads row
|
|
9
|
-
* contents (including legacy routes), changes pragmas, or performs DDL.
|
|
10
|
-
*/
|
|
11
|
-
export declare function legacyRowFramingIssue(database: DatabaseSync, applicationId: number, version: 2 | 3, location: string, currentSchemaSql: string): string | undefined;
|