@astrosheep/square 0.3.12 → 0.3.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/codex-plugin/.codex-plugin/plugin.json +1 -1
- package/codex-plugin/hooks/hooks.json +9 -0
- package/dist/activity-feed.d.ts +14 -0
- package/dist/activity-feed.js +5 -5
- package/dist/activity.d.ts +9 -0
- package/dist/activity.js +111 -90
- package/dist/artifact.d.ts +20 -0
- package/dist/artifact.js +10 -29
- package/dist/automatic-session.d.ts +5 -0
- package/dist/automatic-session.js +86 -0
- package/dist/boundary-presentation.d.ts +5 -0
- package/dist/boundary-presentation.js +5 -4
- package/dist/claude-hook.d.ts +9 -0
- package/dist/claude-hook.js +35 -3
- package/dist/cli/context.d.ts +35 -0
- package/dist/cli/context.js +25 -30
- package/dist/cli/harness-command.d.ts +28 -0
- package/dist/cli/harness-command.js +29 -14
- package/dist/cli/maintenance-commands.d.ts +7 -0
- package/dist/cli/maintenance-commands.js +3 -3
- package/dist/cli/meta-commands.d.ts +5 -0
- package/dist/cli/observation-commands.d.ts +20 -0
- package/dist/cli/observation-commands.js +155 -128
- package/dist/cli/program.d.ts +2 -0
- package/dist/cli/program.js +2 -12
- package/dist/cli/registry.d.ts +9 -0
- package/dist/cli/registry.js +1 -2
- package/dist/cli/square-commands.d.ts +34 -0
- package/dist/cli/square-commands.js +81 -74
- package/dist/cmd/notify-once.d.ts +2 -0
- package/dist/codex-hook.d.ts +10 -0
- package/dist/codex-hook.js +35 -3
- package/dist/decisions.d.ts +92 -0
- package/dist/decisions.js +83 -77
- package/dist/delivery-health.d.ts +21 -0
- package/dist/delivery-health.js +40 -34
- package/dist/delivery.d.ts +81 -0
- package/dist/delivery.js +60 -22
- package/dist/file-lock.d.ts +6 -0
- package/dist/harness-claude.d.ts +19 -0
- package/dist/harness-claude.js +8 -3
- package/dist/harness-codex.d.ts +29 -0
- package/dist/harness-codex.js +2 -2
- package/dist/harness-links.d.ts +17 -0
- package/dist/harness-links.js +2 -2
- package/dist/harness-pi.d.ts +11 -0
- package/dist/harness-pi.js +2 -2
- package/dist/harness-stage.d.ts +6 -0
- package/dist/harness.d.ts +20 -0
- package/dist/harness.js +5 -3
- package/dist/help.d.ts +6 -0
- package/dist/help.js +6 -5
- package/dist/identity.d.ts +13 -0
- package/dist/inbox.d.ts +2 -0
- package/dist/inbox.js +15 -19
- package/dist/index.d.ts +13 -0
- package/dist/index.js +31 -69
- package/dist/landing.d.ts +10 -0
- package/dist/landing.js +95 -0
- package/dist/list.d.ts +1 -0
- package/dist/list.js +40 -18
- package/dist/model.d.ts +147 -0
- package/dist/model.js +12 -2
- package/dist/notifications.d.ts +30 -0
- package/dist/notifications.js +75 -91
- package/dist/open-square.d.ts +11 -0
- package/dist/open-square.js +4 -0
- package/dist/participant-identity.d.ts +1 -0
- package/dist/participant-identity.js +3 -0
- package/dist/paseo-connection.d.ts +15 -0
- package/dist/paseo-delivery.d.ts +14 -0
- package/dist/paseo-state.d.ts +11 -0
- package/dist/paseo-timeline.d.ts +16 -0
- package/dist/presence.d.ts +3 -0
- package/dist/presence.js +42 -0
- package/dist/presentation.d.ts +91 -0
- package/dist/presentation.js +33 -29
- package/dist/presented.d.ts +11 -0
- package/dist/registry.d.ts +58 -0
- package/dist/registry.js +36 -3
- package/dist/routes.d.ts +42 -0
- package/dist/runtime.d.ts +45 -0
- package/dist/runtime.js +42 -34
- package/dist/search.d.ts +11 -0
- package/dist/square-core.d.ts +111 -0
- package/dist/square-core.js +7 -0
- package/dist/square-facade.d.ts +86 -0
- package/dist/square-facade.js +1 -0
- package/dist/square-file-adapter.d.ts +18 -0
- package/dist/square-file-adapter.js +70 -0
- package/dist/square-storage.d.ts +20 -0
- package/dist/square-storage.js +171 -0
- package/dist/square-wiring.d.ts +19 -0
- package/dist/square-wiring.js +70 -0
- package/dist/square.d.ts +2 -0
- package/dist/state-cell.d.ts +14 -0
- package/dist/state-cell.js +1 -0
- package/dist/stream.d.ts +3 -0
- package/dist/stream.js +19 -23
- package/dist/time.d.ts +6 -0
- package/dist/views.d.ts +95 -0
- package/dist/views.js +81 -0
- package/dist/wake-attempts.d.ts +39 -0
- package/dist/wake-evidence.d.ts +12 -0
- package/dist/wake-evidence.js +7 -10
- package/dist/wake-port.d.ts +20 -0
- package/dist/wake-sink.d.ts +12 -0
- package/dist/wakes.d.ts +40 -0
- package/dist/wakes.js +62 -0
- package/dist/watch.d.ts +2 -0
- package/dist/watch.js +89 -210
- package/extensions/square-opencode.js +27 -1
- package/extensions/square-pi.js +13 -0
- package/package.json +5 -2
- package/skills/brainstorm/SKILL.md +26 -26
- package/skills/square/.claude-plugin/plugin.json +1 -1
- package/skills/square/SKILL.md +47 -29
- package/skills/square/hooks/hooks.json +9 -0
- package/skills/square-feedback/SKILL.md +2 -2
- package/dist/compact.js +0 -26
- package/dist/square-application.js +0 -193
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type DirectedNotificationRoute } from './delivery.js';
|
|
2
|
+
import type { WakeAttempt } from './wake-attempts.js';
|
|
3
|
+
export type DeliveryHealthKind = 'awaiting' | 'wake-accepted' | 'wake-unknown' | 'presented-not-delivered' | 'unreachable';
|
|
4
|
+
export interface DeliveryHealthItem {
|
|
5
|
+
squarePath: string;
|
|
6
|
+
recipient: string;
|
|
7
|
+
actIndex: number;
|
|
8
|
+
actor: string;
|
|
9
|
+
at: number;
|
|
10
|
+
ageMs: number;
|
|
11
|
+
route: DirectedNotificationRoute;
|
|
12
|
+
kind: DeliveryHealthKind;
|
|
13
|
+
attempt?: WakeAttempt;
|
|
14
|
+
}
|
|
15
|
+
/** Purely classify current pending attention from the artifact and durable ledgers. */
|
|
16
|
+
export declare function classifyDeliveryHealth(squarePath: string, opts: {
|
|
17
|
+
graceMs: number;
|
|
18
|
+
now?: number;
|
|
19
|
+
env?: NodeJS.ProcessEnv;
|
|
20
|
+
}): Promise<DeliveryHealthItem[]>;
|
|
21
|
+
export declare function doctorDeliveryHealth(squarePath: string, graceMs: number, now?: number, env?: NodeJS.ProcessEnv): Promise<string[]>;
|
package/dist/delivery-health.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import { loadSquare } from './artifact.js';
|
|
2
|
-
import { deriveDeliveryModel, } from './delivery.js';
|
|
3
1
|
import { formatActivityId } from './square-core.js';
|
|
2
|
+
import { participantIdentity } from './presentation.js';
|
|
4
3
|
import { formatDuration } from './time.js';
|
|
5
|
-
import {
|
|
4
|
+
import { wakeEvidence } from './wake-evidence.js';
|
|
5
|
+
import { openSquare } from './square-file-adapter.js';
|
|
6
|
+
import { closeOpenSquare } from './open-square.js';
|
|
7
|
+
import { pendingDeliveries } from './views.js';
|
|
6
8
|
const DISPLAY_ORDER = [
|
|
7
9
|
'awaiting',
|
|
8
10
|
'wake-accepted',
|
|
@@ -12,43 +14,47 @@ const DISPLAY_ORDER = [
|
|
|
12
14
|
];
|
|
13
15
|
const ACTIONABLE = new Set(['wake-unknown', 'unreachable']);
|
|
14
16
|
/** Purely classify current pending attention from the artifact and durable ledgers. */
|
|
15
|
-
export function classifyDeliveryHealth(squarePath, opts) {
|
|
17
|
+
export async function classifyDeliveryHealth(squarePath, opts) {
|
|
16
18
|
const now = opts.now ?? Date.now();
|
|
17
19
|
const env = opts.env ?? process.env;
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
? '
|
|
27
|
-
: evidence.terminal?.outcome === '
|
|
28
|
-
? 'wake-
|
|
29
|
-
:
|
|
30
|
-
? '
|
|
31
|
-
:
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
20
|
+
const square = await openSquare(squarePath, { clock: () => now });
|
|
21
|
+
const pending = await pendingDeliveries(square).finally(() => closeOpenSquare(square));
|
|
22
|
+
const items = [];
|
|
23
|
+
for (const delivery of pending) {
|
|
24
|
+
for (const note of delivery.notifications) {
|
|
25
|
+
const ageMs = Math.max(0, now - note.item.at);
|
|
26
|
+
const evidence = await wakeEvidence(squarePath, note.recipient, note.item.index, now, env);
|
|
27
|
+
const kind = evidence.presented
|
|
28
|
+
? 'presented-not-delivered'
|
|
29
|
+
: evidence.terminal?.outcome === 'accepted'
|
|
30
|
+
? 'wake-accepted'
|
|
31
|
+
: evidence.terminal?.outcome === 'unknown'
|
|
32
|
+
? 'wake-unknown'
|
|
33
|
+
: ageMs > opts.graceMs && evidence.attemptableRoutes.length === 0
|
|
34
|
+
? 'unreachable'
|
|
35
|
+
: 'awaiting';
|
|
36
|
+
const attempt = evidence.terminal ?? evidence.attempts.at(-1);
|
|
37
|
+
items.push({
|
|
38
|
+
squarePath,
|
|
39
|
+
recipient: note.recipient,
|
|
40
|
+
actIndex: note.item.index,
|
|
41
|
+
actor: note.item.actor,
|
|
42
|
+
at: note.item.at,
|
|
43
|
+
ageMs,
|
|
44
|
+
route: note.route,
|
|
45
|
+
kind,
|
|
46
|
+
...(attempt === undefined ? {} : { attempt }),
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return items;
|
|
45
51
|
}
|
|
46
52
|
function formatItem(item) {
|
|
47
53
|
const evidence = item.attempt?.signature === undefined ? '' : ` · ${item.attempt.signature}`;
|
|
48
|
-
return ` · ${formatActivityId(item.actIndex)} →
|
|
54
|
+
return ` · ${formatActivityId(item.actIndex)} → ${participantIdentity(item.recipient)} from ${participantIdentity(item.actor)} · ${formatDuration(item.ageMs)}${evidence}`;
|
|
49
55
|
}
|
|
50
|
-
export function doctorDeliveryHealth(squarePath, graceMs, now = Date.now(), env = process.env) {
|
|
51
|
-
const items = classifyDeliveryHealth(squarePath, { graceMs, now, env });
|
|
56
|
+
export async function doctorDeliveryHealth(squarePath, graceMs, now = Date.now(), env = process.env) {
|
|
57
|
+
const items = await classifyDeliveryHealth(squarePath, { graceMs, now, env });
|
|
52
58
|
if (items.length === 0)
|
|
53
59
|
return ['✓ no pending delivery attention'];
|
|
54
60
|
const out = [`· delivery attention · ${items.length} pending`];
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { type DeliveryReceipt, type DirectedNotificationRoute, type SquareState, type SquareRuntimeState, type StoredAct, type WatchLease, type WatchLeaseFilter, type WakeRouteKind, type Reach } from './model.js';
|
|
2
|
+
export type { DirectedNotificationRoute } from './model.js';
|
|
3
|
+
export type SayItem = StoredAct & {
|
|
4
|
+
kind: 'say';
|
|
5
|
+
};
|
|
6
|
+
export declare function notificationMessageId(squarePath: string, actIndex: number): string;
|
|
7
|
+
export interface PlannedNotification {
|
|
8
|
+
item: SayItem;
|
|
9
|
+
recipient: string;
|
|
10
|
+
route: DirectedNotificationRoute;
|
|
11
|
+
}
|
|
12
|
+
export interface WakeRequest {
|
|
13
|
+
squarePath: string;
|
|
14
|
+
actIndex: number;
|
|
15
|
+
recipient: string;
|
|
16
|
+
actor: string;
|
|
17
|
+
route: DirectedNotificationRoute;
|
|
18
|
+
}
|
|
19
|
+
export type WakeDispatchResult = {
|
|
20
|
+
outcome: 'accepted';
|
|
21
|
+
} | {
|
|
22
|
+
outcome: 'unknown';
|
|
23
|
+
signature: string;
|
|
24
|
+
message: string;
|
|
25
|
+
diagnostic?: unknown;
|
|
26
|
+
} | {
|
|
27
|
+
outcome: 'failed';
|
|
28
|
+
signature: string;
|
|
29
|
+
message: string;
|
|
30
|
+
diagnostic?: unknown;
|
|
31
|
+
} | {
|
|
32
|
+
outcome: 'cancelled';
|
|
33
|
+
};
|
|
34
|
+
export interface WakeAdapter {
|
|
35
|
+
readonly kind: WakeRouteKind;
|
|
36
|
+
dispatch(address: Readonly<Record<string, string>>, payload: string, beforeSend: () => Promise<boolean>): Promise<WakeDispatchResult>;
|
|
37
|
+
}
|
|
38
|
+
export interface RoutedNotification {
|
|
39
|
+
actor: string;
|
|
40
|
+
body: string;
|
|
41
|
+
route: DirectedNotificationRoute;
|
|
42
|
+
}
|
|
43
|
+
export interface CatchFilterShape {
|
|
44
|
+
actor: string;
|
|
45
|
+
body: string;
|
|
46
|
+
reach?: Reach;
|
|
47
|
+
}
|
|
48
|
+
export interface DeliveryModel {
|
|
49
|
+
plan(item: StoredAct): PlannedNotification[];
|
|
50
|
+
pendingFor(recipient: string): PlannedNotification[];
|
|
51
|
+
}
|
|
52
|
+
/** Delivery receipts are the only durable acknowledgement of directed attention. */
|
|
53
|
+
export declare function deliveryReceipt(squareState: SquareState, name: string, actOrIndex: StoredAct | number): DeliveryReceipt | undefined;
|
|
54
|
+
export declare function deliveryReceiptFromRuntime(runtime: SquareRuntimeState, recipient: string, actOrIndex: StoredAct | number): DeliveryReceipt | undefined;
|
|
55
|
+
export declare function isDeliveryDelivered(squareState: SquareState, name: string, actOrIndex: StoredAct | number): boolean;
|
|
56
|
+
export declare function recordDeliveredDelivery(squareState: SquareState, name: string, actOrIndex: StoredAct | number, receipt: Omit<DeliveryReceipt, 'status'>): boolean;
|
|
57
|
+
export declare function recordDeliveredRuntime(runtime: SquareRuntimeState, recipient: string, actOrIndex: StoredAct | number, receipt: Omit<DeliveryReceipt, 'status'>): boolean;
|
|
58
|
+
export declare function markDeliveredDelivery(squareState: SquareState, name: string, actOrIndex: StoredAct | number, at?: number): boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Derive delivery behavior once from the decoded Square state.
|
|
61
|
+
* All consumers share these targets instead of reinterpreting artifact text or cursor state.
|
|
62
|
+
*/
|
|
63
|
+
export declare function deriveDeliveryModel(squareState: SquareState): DeliveryModel;
|
|
64
|
+
export declare function planActNotifications(squareState: SquareState, item: StoredAct): PlannedNotification[];
|
|
65
|
+
/** Mark only the directed notifications selected by the canonical pending projection. */
|
|
66
|
+
export declare function markDeliveredNotifications(squareState: SquareState, recipient: string, delivered: StoredAct[], at?: number): boolean;
|
|
67
|
+
/** Canonical say-activity filter shared by catch selection and hook ownership. */
|
|
68
|
+
export declare function matchesCatchFilter(activity: CatchFilterShape, filter: WatchLeaseFilter): boolean;
|
|
69
|
+
/** True only when the live catch's own filters would deliver this notification. */
|
|
70
|
+
export declare function leaseOwnsNotification(lease: WatchLease, notification: RoutedNotification): boolean;
|
|
71
|
+
export type SquareRoute = Readonly<unknown>;
|
|
72
|
+
interface ParsedSquareRoute {
|
|
73
|
+
readonly v: 1;
|
|
74
|
+
readonly squarePath: string;
|
|
75
|
+
readonly name: string;
|
|
76
|
+
}
|
|
77
|
+
export declare function parseSquareRoute(value: unknown): ParsedSquareRoute;
|
|
78
|
+
export declare function captureRoute(input: {
|
|
79
|
+
cwd: string;
|
|
80
|
+
env?: NodeJS.ProcessEnv;
|
|
81
|
+
}): SquareRoute | null;
|
package/dist/delivery.js
CHANGED
|
@@ -1,24 +1,26 @@
|
|
|
1
|
-
import
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { SquareError, findParticipantName, sameName, } from './model.js';
|
|
3
|
+
import { canonicalSquarePath, localParticipantName } from './registry.js';
|
|
2
4
|
import { audienceOf, formatActivityId, resolveAudience } from './square-core.js';
|
|
3
5
|
import { actId, isCurrentlyJoined, lastJoinIndex, matchesMentionTarget, resolveRosterName, rosterNames } from './runtime.js';
|
|
4
6
|
export function notificationMessageId(squarePath, actIndex) {
|
|
5
7
|
return `square:${squarePath}#${formatActivityId(actIndex)}`;
|
|
6
8
|
}
|
|
7
|
-
function canonicalRecipient(
|
|
8
|
-
return resolveRosterName(
|
|
9
|
+
function canonicalRecipient(squareState, name) {
|
|
10
|
+
return resolveRosterName(squareState, name) ?? name;
|
|
9
11
|
}
|
|
10
12
|
/** Delivery receipts are the only durable acknowledgement of directed attention. */
|
|
11
|
-
export function deliveryReceipt(
|
|
12
|
-
return deliveryReceiptFromRuntime(
|
|
13
|
+
export function deliveryReceipt(squareState, name, actOrIndex) {
|
|
14
|
+
return deliveryReceiptFromRuntime(squareState.runtime, canonicalRecipient(squareState, name), actOrIndex);
|
|
13
15
|
}
|
|
14
16
|
export function deliveryReceiptFromRuntime(runtime, recipient, actOrIndex) {
|
|
15
17
|
return runtime.deliveryReceipts[recipient]?.[actId(actOrIndex)];
|
|
16
18
|
}
|
|
17
|
-
export function isDeliveryDelivered(
|
|
18
|
-
return deliveryReceipt(
|
|
19
|
+
export function isDeliveryDelivered(squareState, name, actOrIndex) {
|
|
20
|
+
return deliveryReceipt(squareState, name, actOrIndex)?.status === 'delivered';
|
|
19
21
|
}
|
|
20
|
-
export function recordDeliveredDelivery(
|
|
21
|
-
return recordDeliveredRuntime(
|
|
22
|
+
export function recordDeliveredDelivery(squareState, name, actOrIndex, receipt) {
|
|
23
|
+
return recordDeliveredRuntime(squareState.runtime, canonicalRecipient(squareState, name), actOrIndex, receipt);
|
|
22
24
|
}
|
|
23
25
|
export function recordDeliveredRuntime(runtime, recipient, actOrIndex, receipt) {
|
|
24
26
|
const id = actId(actOrIndex);
|
|
@@ -27,15 +29,15 @@ export function recordDeliveredRuntime(runtime, recipient, actOrIndex, receipt)
|
|
|
27
29
|
(runtime.deliveryReceipts[recipient] ??= {})[id] = { status: 'delivered', ...receipt };
|
|
28
30
|
return true;
|
|
29
31
|
}
|
|
30
|
-
export function markDeliveredDelivery(
|
|
31
|
-
return recordDeliveredDelivery(
|
|
32
|
+
export function markDeliveredDelivery(squareState, name, actOrIndex, at = Date.now()) {
|
|
33
|
+
return recordDeliveredDelivery(squareState, name, actOrIndex, { at });
|
|
32
34
|
}
|
|
33
35
|
/**
|
|
34
|
-
* Derive delivery behavior once from the
|
|
36
|
+
* Derive delivery behavior once from the decoded Square state.
|
|
35
37
|
* All consumers share these targets instead of reinterpreting artifact text or cursor state.
|
|
36
38
|
*/
|
|
37
|
-
export function deriveDeliveryModel(
|
|
38
|
-
const roster = rosterNames(
|
|
39
|
+
export function deriveDeliveryModel(squareState) {
|
|
40
|
+
const roster = rosterNames(squareState).filter((name) => isCurrentlyJoined(squareState.acts, name));
|
|
39
41
|
let pendingByRecipient;
|
|
40
42
|
function plan(item) {
|
|
41
43
|
if (item.kind !== 'say')
|
|
@@ -52,8 +54,8 @@ export function deriveDeliveryModel(doc) {
|
|
|
52
54
|
return [];
|
|
53
55
|
if (pendingByRecipient === undefined) {
|
|
54
56
|
pendingByRecipient = new Map(roster.map((name) => [name, []]));
|
|
55
|
-
const joinedAfter = new Map(roster.map((name) => [name, lastJoinIndex(
|
|
56
|
-
for (const act of
|
|
57
|
+
const joinedAfter = new Map(roster.map((name) => [name, lastJoinIndex(squareState.acts, name)]));
|
|
58
|
+
for (const act of squareState.acts) {
|
|
57
59
|
if (act.kind !== 'say')
|
|
58
60
|
continue;
|
|
59
61
|
const audience = audienceOf(act);
|
|
@@ -63,7 +65,7 @@ export function deriveDeliveryModel(doc) {
|
|
|
63
65
|
const joinedAt = joinedAfter.get(planned.recipient);
|
|
64
66
|
if (joinedAt === undefined || act.index <= joinedAt)
|
|
65
67
|
continue;
|
|
66
|
-
if (isDeliveryDelivered(
|
|
68
|
+
if (isDeliveryDelivered(squareState, planned.recipient, act.index))
|
|
67
69
|
continue;
|
|
68
70
|
pendingByRecipient.get(planned.recipient)?.push(planned);
|
|
69
71
|
}
|
|
@@ -73,17 +75,17 @@ export function deriveDeliveryModel(doc) {
|
|
|
73
75
|
}
|
|
74
76
|
return { plan, pendingFor };
|
|
75
77
|
}
|
|
76
|
-
export function planActNotifications(
|
|
77
|
-
return deriveDeliveryModel(
|
|
78
|
+
export function planActNotifications(squareState, item) {
|
|
79
|
+
return deriveDeliveryModel(squareState).plan(item);
|
|
78
80
|
}
|
|
79
81
|
/** Mark only the directed notifications selected by the canonical pending projection. */
|
|
80
|
-
export function markDeliveredNotifications(
|
|
82
|
+
export function markDeliveredNotifications(squareState, recipient, delivered, at = Date.now()) {
|
|
81
83
|
const deliveredIndexes = new Set(delivered.map((item) => item.index));
|
|
82
84
|
let changed = false;
|
|
83
|
-
for (const notification of deriveDeliveryModel(
|
|
85
|
+
for (const notification of deriveDeliveryModel(squareState).pendingFor(recipient)) {
|
|
84
86
|
if (!deliveredIndexes.has(notification.item.index))
|
|
85
87
|
continue;
|
|
86
|
-
changed = recordDeliveredDelivery(
|
|
88
|
+
changed = recordDeliveredDelivery(squareState, notification.recipient, notification.item.index, { at }) || changed;
|
|
87
89
|
}
|
|
88
90
|
return changed;
|
|
89
91
|
}
|
|
@@ -105,3 +107,39 @@ export function leaseOwnsNotification(lease, notification) {
|
|
|
105
107
|
...(notification.route === 'bell' ? { reach: 'bell' } : {}),
|
|
106
108
|
}, lease.filter ?? {});
|
|
107
109
|
}
|
|
110
|
+
const SQUARE_ROUTE_KEYS = ['name', 'squarePath', 'v'];
|
|
111
|
+
function publicSquareFromCwd(cwd) {
|
|
112
|
+
return path.resolve(cwd, '.square', 'PUBLIC.square');
|
|
113
|
+
}
|
|
114
|
+
export function parseSquareRoute(value) {
|
|
115
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
|
|
116
|
+
throw new SquareError('invalid_args', 'Malformed Square route');
|
|
117
|
+
}
|
|
118
|
+
const record = value;
|
|
119
|
+
const keys = Object.keys(record).sort();
|
|
120
|
+
if (keys.length !== SQUARE_ROUTE_KEYS.length || keys.some((key, index) => key !== SQUARE_ROUTE_KEYS[index])) {
|
|
121
|
+
throw new SquareError('invalid_args', 'Malformed Square route');
|
|
122
|
+
}
|
|
123
|
+
if (record.v !== 1 ||
|
|
124
|
+
typeof record.squarePath !== 'string' || record.squarePath === '' ||
|
|
125
|
+
typeof record.name !== 'string' || record.name === '') {
|
|
126
|
+
throw new SquareError('invalid_args', 'Malformed Square route');
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
v: 1,
|
|
130
|
+
squarePath: record.squarePath,
|
|
131
|
+
name: record.name,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
export function captureRoute(input) {
|
|
135
|
+
const env = input.env ?? process.env;
|
|
136
|
+
const squarePath = canonicalSquarePath(publicSquareFromCwd(input.cwd));
|
|
137
|
+
const name = localParticipantName(squarePath, env);
|
|
138
|
+
if (name === undefined)
|
|
139
|
+
return null;
|
|
140
|
+
return Object.freeze({
|
|
141
|
+
v: 1,
|
|
142
|
+
squarePath,
|
|
143
|
+
name,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export interface FileLockOptions {
|
|
2
|
+
retryMs: number;
|
|
3
|
+
staleMs: number;
|
|
4
|
+
}
|
|
5
|
+
export declare function withFileLockSync<T>(lockPath: string, options: FileLockOptions, fn: () => T): T;
|
|
6
|
+
export declare function withFileLock<T>(lockPath: string, options: FileLockOptions, fn: () => T | Promise<T>): Promise<T>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare const CLAUDE_PLUGIN_ID: "square@astrosheep";
|
|
2
|
+
export declare const CLAUDE_MARKETPLACE_NAME: "astrosheep";
|
|
3
|
+
export interface ClaudeCommandResult {
|
|
4
|
+
status: number;
|
|
5
|
+
stdout: string;
|
|
6
|
+
stderr: string;
|
|
7
|
+
}
|
|
8
|
+
export type ClaudeCommandRunner = (homeDir: string, args: string[]) => ClaudeCommandResult;
|
|
9
|
+
export interface ClaudeInstallResult {
|
|
10
|
+
marketplaceRoot: string;
|
|
11
|
+
pluginRoot: string;
|
|
12
|
+
}
|
|
13
|
+
export declare function claudeMarketplaceRoot(homeDir: string): string;
|
|
14
|
+
export declare function installClaudePlugin(homeDir: string, run?: ClaudeCommandRunner): Promise<ClaudeInstallResult>;
|
|
15
|
+
export declare function uninstallClaudePlugin(homeDir: string, run?: ClaudeCommandRunner): Promise<{
|
|
16
|
+
paths: string[];
|
|
17
|
+
notes: string[];
|
|
18
|
+
}>;
|
|
19
|
+
export declare function doctorClaudePlugin(homeDir: string, run?: ClaudeCommandRunner): Promise<string[]>;
|
package/dist/harness-claude.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { spawnSync } from 'node:child_process';
|
|
2
1
|
import fs from 'node:fs';
|
|
3
2
|
import path from 'node:path';
|
|
4
3
|
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import crossSpawn from 'cross-spawn';
|
|
5
5
|
import { SQUARE_IDENTITY } from './identity.js';
|
|
6
6
|
import { stageReplacement } from './harness-stage.js';
|
|
7
7
|
export const CLAUDE_PLUGIN_ID = SQUARE_IDENTITY.pluginId;
|
|
@@ -10,11 +10,16 @@ export function claudeMarketplaceRoot(homeDir) {
|
|
|
10
10
|
return path.join(homeDir, '.square', 'claude', 'marketplaces', CLAUDE_MARKETPLACE_NAME);
|
|
11
11
|
}
|
|
12
12
|
function runClaude(homeDir, args) {
|
|
13
|
-
const
|
|
13
|
+
const command = process.env.SQUARE_CLAUDE_BIN || 'claude';
|
|
14
|
+
const result = crossSpawn.sync(command, args, {
|
|
14
15
|
encoding: 'utf8', env: { ...process.env, HOME: homeDir, CLAUDE_CONFIG_DIR: path.join(homeDir, '.claude') }, timeout: 30_000,
|
|
15
16
|
});
|
|
16
|
-
if (result.error)
|
|
17
|
+
if (result.error) {
|
|
18
|
+
if (result.error.code === 'ENOENT') {
|
|
19
|
+
throw new Error(`Claude CLI not found: ${command}. Install Claude Code or set SQUARE_CLAUDE_BIN to its executable path.`);
|
|
20
|
+
}
|
|
17
21
|
throw result.error;
|
|
22
|
+
}
|
|
18
23
|
return { status: result.status ?? 1, stdout: result.stdout || '', stderr: result.stderr || '' };
|
|
19
24
|
}
|
|
20
25
|
function requireSuccess(result, operation, allowMissing = false) {
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export declare const CODEX_HOOK_COMMAND: "square codex-hook";
|
|
2
|
+
export declare const SQUARE_CODEX_MARKER: "square-codex-hook";
|
|
3
|
+
export declare const CODEX_PLUGIN_ID: "square@astrosheep";
|
|
4
|
+
export declare const CODEX_MARKETPLACE_NAME: "astrosheep";
|
|
5
|
+
export interface CodexCommandResult {
|
|
6
|
+
status: number;
|
|
7
|
+
stdout: string;
|
|
8
|
+
stderr: string;
|
|
9
|
+
}
|
|
10
|
+
export type CodexCommandRunner = (homeDir: string, args: string[], codexHomeDir?: string) => CodexCommandResult;
|
|
11
|
+
export interface CodexInstallResult {
|
|
12
|
+
configPath: string;
|
|
13
|
+
marketplaceRoot: string;
|
|
14
|
+
pluginRoot: string;
|
|
15
|
+
installedPath?: string;
|
|
16
|
+
notes: string[];
|
|
17
|
+
}
|
|
18
|
+
export declare function codexMarketplaceRoot(homeDir: string): string;
|
|
19
|
+
export declare function codexPluginRoot(homeDir: string, marketplaceRoot?: string): string;
|
|
20
|
+
export declare function codexPluginHooksPath(homeDir: string, marketplaceRoot?: string): string;
|
|
21
|
+
export declare function codexHomeHooksPath(homeDir: string, codexHomeDir?: string): string;
|
|
22
|
+
export declare function codexConfigPath(homeDir: string, codexHomeDir?: string): string;
|
|
23
|
+
export declare function upsertTomlSectionKey(text: string, section: string, key: string, value: string): string;
|
|
24
|
+
export declare function installCodexPlugin(homeDir: string, run?: CodexCommandRunner, codexHomeDir?: string): Promise<CodexInstallResult>;
|
|
25
|
+
export declare function uninstallCodexPlugin(homeDir: string, run?: CodexCommandRunner, codexHomeDir?: string): Promise<{
|
|
26
|
+
paths: string[];
|
|
27
|
+
notes: string[];
|
|
28
|
+
}>;
|
|
29
|
+
export declare function doctorCodexPlugin(homeDir: string, run?: CodexCommandRunner, codexHomeDir?: string): Promise<string[]>;
|
package/dist/harness-codex.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { spawnSync } from 'node:child_process';
|
|
2
1
|
import fs from 'node:fs';
|
|
3
2
|
import path from 'node:path';
|
|
4
3
|
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import crossSpawn from 'cross-spawn';
|
|
5
5
|
import { SQUARE_IDENTITY } from './identity.js';
|
|
6
6
|
import { stageReplacement } from './harness-stage.js';
|
|
7
7
|
export const CODEX_HOOK_COMMAND = SQUARE_IDENTITY.hookCommand;
|
|
@@ -46,7 +46,7 @@ function codexHome(homeDir, codexHomeDir) {
|
|
|
46
46
|
export function codexHomeHooksPath(homeDir, codexHomeDir) { return path.join(codexHome(homeDir, codexHomeDir), 'hooks.json'); }
|
|
47
47
|
export function codexConfigPath(homeDir, codexHomeDir) { return path.join(codexHome(homeDir, codexHomeDir), 'config.toml'); }
|
|
48
48
|
function runCodex(homeDir, args, codexHomeDir) {
|
|
49
|
-
const result =
|
|
49
|
+
const result = crossSpawn.sync(process.env.SQUARE_CODEX_BIN || 'codex', args, {
|
|
50
50
|
encoding: 'utf8', env: { ...process.env, HOME: homeDir, CODEX_HOME: codexHome(homeDir, codexHomeDir) }, timeout: 30_000,
|
|
51
51
|
});
|
|
52
52
|
if (result.error)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface HarnessLink {
|
|
2
|
+
source: string;
|
|
3
|
+
target: string;
|
|
4
|
+
kind?: 'skill' | 'extension';
|
|
5
|
+
}
|
|
6
|
+
export declare function installHarnessLinks(links: HarnessLink[], force?: boolean): string[];
|
|
7
|
+
export declare function uninstallHarnessLinks(links: HarnessLink[]): string[];
|
|
8
|
+
export declare function doctorHarnessLinks(links: HarnessLink[]): string[];
|
|
9
|
+
export type OpenCodeCommandRunner = (homeDir: string, args: string[]) => {
|
|
10
|
+
status: number;
|
|
11
|
+
stdout: string;
|
|
12
|
+
stderr: string;
|
|
13
|
+
};
|
|
14
|
+
/** Verify that OpenCode accepts its resolved runtime configuration after links are installed. */
|
|
15
|
+
export declare function verifyOpenCodeRuntime(homeDir: string, run?: OpenCodeCommandRunner): string;
|
|
16
|
+
export declare function skillLinks(homeDir?: string, parents?: Array<'.claude' | '.agents'>): HarnessLink[];
|
|
17
|
+
export declare function opencodeExtensionLink(homeDir?: string): HarnessLink;
|
package/dist/harness-links.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import os from 'node:os';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
-
import { spawnSync } from 'node:child_process';
|
|
5
4
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
5
|
+
import crossSpawn from 'cross-spawn';
|
|
6
6
|
function packageRoot() {
|
|
7
7
|
// Emitted modules live in dist; package assets are one level above them.
|
|
8
8
|
return fileURLToPath(new URL('../', import.meta.url));
|
|
@@ -74,7 +74,7 @@ export function doctorHarnessLinks(links) {
|
|
|
74
74
|
: `○ Square ${link.kind ?? 'link'} missing ${link.target}`);
|
|
75
75
|
}
|
|
76
76
|
function runOpenCode(homeDir, args) {
|
|
77
|
-
const result =
|
|
77
|
+
const result = crossSpawn.sync(process.env.SQUARE_OPENCODE_BIN || 'opencode', args, {
|
|
78
78
|
encoding: 'utf8',
|
|
79
79
|
env: { ...process.env, HOME: homeDir, XDG_CONFIG_HOME: process.env.XDG_CONFIG_HOME ?? path.join(homeDir, '.config') },
|
|
80
80
|
timeout: 30_000,
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface PiCommandResult {
|
|
2
|
+
status: number;
|
|
3
|
+
stdout: string;
|
|
4
|
+
stderr: string;
|
|
5
|
+
}
|
|
6
|
+
export type PiCommandRunner = (homeDir: string, args: string[]) => PiCommandResult;
|
|
7
|
+
export declare function piPackageSource(): string;
|
|
8
|
+
export declare function piPackageRoot(homeDir: string): string;
|
|
9
|
+
export declare function installPiPackage(homeDir: string, run?: PiCommandRunner): string[];
|
|
10
|
+
export declare function uninstallPiPackage(homeDir: string, run?: PiCommandRunner): string[];
|
|
11
|
+
export declare function doctorPiPackage(homeDir: string, run?: PiCommandRunner): string[];
|
package/dist/harness-pi.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import
|
|
3
|
+
import crossSpawn from 'cross-spawn';
|
|
4
4
|
import { SQUARE_IDENTITY } from './identity.js';
|
|
5
5
|
export function piPackageSource() {
|
|
6
6
|
return `npm:${SQUARE_IDENTITY.packageName}@${SQUARE_IDENTITY.packageVersion}`;
|
|
@@ -9,7 +9,7 @@ export function piPackageRoot(homeDir) {
|
|
|
9
9
|
return path.join(homeDir, '.pi', 'agent', 'npm', 'node_modules', ...SQUARE_IDENTITY.packageName.split('/'));
|
|
10
10
|
}
|
|
11
11
|
function runPi(homeDir, args) {
|
|
12
|
-
const result =
|
|
12
|
+
const result = crossSpawn.sync(process.env.SQUARE_PI_BIN || 'pi', args, {
|
|
13
13
|
encoding: 'utf8',
|
|
14
14
|
env: { ...process.env, HOME: homeDir },
|
|
15
15
|
timeout: 30_000,
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export interface StagedReplacement {
|
|
2
|
+
rollback(): void;
|
|
3
|
+
finalize(): void;
|
|
4
|
+
}
|
|
5
|
+
/** Replace a file or directory while retaining the previous value for rollback. */
|
|
6
|
+
export declare function stageReplacement(root: string, populate: (stage: string) => void): StagedReplacement;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type HarnessTargetName = 'claude' | 'codex' | 'opencode' | 'pi' | 'delivery';
|
|
2
|
+
export type HarnessAction = 'install' | 'uninstall' | 'doctor';
|
|
3
|
+
export interface HarnessTargetContext {
|
|
4
|
+
homeDir: string;
|
|
5
|
+
squarePath?: string;
|
|
6
|
+
force: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface HarnessTargetResult {
|
|
9
|
+
lines: string[];
|
|
10
|
+
notes: string[];
|
|
11
|
+
}
|
|
12
|
+
export interface HarnessTarget {
|
|
13
|
+
name: HarnessTargetName;
|
|
14
|
+
capabilities: readonly HarnessAction[];
|
|
15
|
+
install?(context: HarnessTargetContext): Promise<HarnessTargetResult> | HarnessTargetResult;
|
|
16
|
+
uninstall?(context: HarnessTargetContext): Promise<HarnessTargetResult> | HarnessTargetResult;
|
|
17
|
+
doctor?(context: HarnessTargetContext): Promise<HarnessTargetResult> | HarnessTargetResult;
|
|
18
|
+
}
|
|
19
|
+
export declare function harnessTargets(): readonly HarnessTarget[];
|
|
20
|
+
export declare function executeHarnessTarget(targetName: string, action: HarnessAction, context: HarnessTargetContext): Promise<HarnessTargetResult>;
|
package/dist/harness.js
CHANGED
|
@@ -79,9 +79,11 @@ const TARGETS = [
|
|
|
79
79
|
{
|
|
80
80
|
name: 'delivery',
|
|
81
81
|
capabilities: ['doctor'],
|
|
82
|
-
doctor
|
|
83
|
-
|
|
84
|
-
|
|
82
|
+
async doctor({ squarePath }) {
|
|
83
|
+
return result(readableSquarePath(squarePath)
|
|
84
|
+
? await doctorDeliveryHealth(squarePath, wakeGraceMs())
|
|
85
|
+
: ['○ delivery health skipped (no readable square path)']);
|
|
86
|
+
},
|
|
85
87
|
},
|
|
86
88
|
];
|
|
87
89
|
export function harnessTargets() {
|
package/dist/help.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare function renderGlobalHelp(): string;
|
|
2
|
+
export declare function renderSubcommandHelp(command: string): string | undefined;
|
|
3
|
+
export declare function helpRequest(rawArgs: string[]): {
|
|
4
|
+
command?: string;
|
|
5
|
+
} | undefined;
|
|
6
|
+
export declare function commandUsageHint(command: string | undefined): string;
|
package/dist/help.js
CHANGED
|
@@ -40,7 +40,7 @@ const COMMANDS = [
|
|
|
40
40
|
{
|
|
41
41
|
names: ['history'], usage: '[--as <name>] history [filters] [output]', usesSquare: true, group: 'participant',
|
|
42
42
|
summary: 'Read or search the archive without changing what you have caught.',
|
|
43
|
-
details: ['Filters:', ' --from <names> Match activities from participants.', ' --since <time>, --until <time> Match a time window.', ' --grep <regex> | --fixed <s> Search ids,
|
|
43
|
+
details: ['Filters:', ' --from <names> Match activities from participants.', ' --since <time>, --until <time> Match a time window.', ' --grep <regex> | --fixed <s> Search activity ids, participants, and original bodies.', ' --mention <name> Match mentions.', ' --pending Match attention waiting for --as <name>.', ' --at <ids> Center on comma-separated activity ids; may repeat.', ' -B, -A, -C <N> Set non-negative context around every --at coordinate.', ' --after <id> Match activities after an id.', '', 'Results:', ' --limit <N> | --all Bound the newest matches (default 10).', ' --order <asc|desc> Set display order (default oldest first).', '', 'Output:', ' --full --json --format <fields>', ' Without --as, the default view shows complete activity bodies.'],
|
|
44
44
|
},
|
|
45
45
|
{ names: ['status'], usage: '[--as <name>] status', usesSquare: true, group: 'participant', summary: 'Show who is present and what happened most recently.' },
|
|
46
46
|
{ names: ['participants'], usage: 'participants', usesSquare: true, group: 'host', summary: 'Show the full participant roster and current states.' },
|
|
@@ -61,7 +61,6 @@ const COMMANDS = [
|
|
|
61
61
|
summary: 'Diagnose installed agent-host support.',
|
|
62
62
|
details: ['Targets:', ' claude, codex, opencode, pi Diagnose one installed adapter.', ' delivery Diagnose delivery for the selected square.'],
|
|
63
63
|
},
|
|
64
|
-
{ names: ['compact'], usage: 'compact [--keep N]', usesSquare: true, group: 'host', summary: 'Move older activity out of the working artifact while keeping the latest N.' },
|
|
65
64
|
{
|
|
66
65
|
names: ['doctor'], usage: 'doctor', usesSquare: true, group: 'maintenance',
|
|
67
66
|
summary: 'Validate binary artifact integrity.',
|
|
@@ -76,7 +75,7 @@ function isHelpFlag(value) {
|
|
|
76
75
|
export function renderGlobalHelp() {
|
|
77
76
|
const groups = [
|
|
78
77
|
{ key: 'participant', title: 'In the square:', order: ['join', 'express', 'catch', 'history', 'status', 'hold', 'resume', 'done'] },
|
|
79
|
-
{ key: 'host', title: 'Prepare and manage:', order: ['build', 'list', 'participants'
|
|
78
|
+
{ key: 'host', title: 'Prepare and manage:', order: ['build', 'list', 'participants'] },
|
|
80
79
|
{ key: 'maintenance', title: 'Setup:', order: ['install', 'uninstall', 'doctor'] },
|
|
81
80
|
];
|
|
82
81
|
const commandLines = groups.flatMap(({ key, title, order }) => [
|
|
@@ -88,7 +87,9 @@ export function renderGlobalHelp() {
|
|
|
88
87
|
'',
|
|
89
88
|
]);
|
|
90
89
|
return [
|
|
91
|
-
'Usage: square [--location <
|
|
90
|
+
'Usage: square [--location <square>] [--as <name>] <command> [args...]',
|
|
91
|
+
'Environment: SQUARE_LOCATION sets the CLI location; SQUARE_PARTICIPANT_NAME sets the participant name.',
|
|
92
|
+
'CLI flags override environment values. Automatic provider sessions use only .square/PUBLIC.square.',
|
|
92
93
|
'',
|
|
93
94
|
...commandLines,
|
|
94
95
|
"Run 'square <command> --help' for command options.",
|
|
@@ -102,7 +103,7 @@ export function renderSubcommandHelp(command) {
|
|
|
102
103
|
const aliases = definition.names.filter((name) => name !== command);
|
|
103
104
|
const usage = definition.usage.replace('{command}', command);
|
|
104
105
|
return [
|
|
105
|
-
`Usage: square ${definition.usesSquare ? '[--location <
|
|
106
|
+
`Usage: square ${definition.usesSquare ? '[--location <square>] ' : ''}${usage}`,
|
|
106
107
|
...(aliases.length > 0 ? [`Aliases: ${aliases.join(', ')}`] : []),
|
|
107
108
|
'',
|
|
108
109
|
definition.summary,
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare const SQUARE_IDENTITY: Readonly<{
|
|
2
|
+
publisher: "astrosheep";
|
|
3
|
+
packageName: "@astrosheep/square";
|
|
4
|
+
packageVersion: string;
|
|
5
|
+
productName: "Square";
|
|
6
|
+
pluginName: "square";
|
|
7
|
+
marketplaceName: "astrosheep";
|
|
8
|
+
cliName: "square";
|
|
9
|
+
pluginId: "square@astrosheep";
|
|
10
|
+
hookCommand: "square codex-hook";
|
|
11
|
+
hookMarker: "square-codex-hook";
|
|
12
|
+
clientName: "astrosheep_square";
|
|
13
|
+
}>;
|
package/dist/inbox.d.ts
ADDED