@astrosheep/square 0.3.10 → 0.3.12
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/dist/activity.js +6 -7
- package/dist/artifact.js +337 -618
- package/dist/boundary-presentation.js +1 -1
- package/dist/cli/context.js +3 -3
- package/dist/cli/harness-command.js +1 -1
- package/dist/cli/maintenance-commands.js +12 -58
- package/dist/cli/observation-commands.js +14 -34
- package/dist/cli/program.js +3 -6
- package/dist/cli/registry.js +1 -2
- package/dist/cli/square-commands.js +39 -20
- package/dist/cmd/notify-once.js +5 -15
- package/dist/compact.js +4 -4
- package/dist/decisions.js +21 -7
- package/dist/delivery-health.js +56 -136
- package/dist/delivery.js +11 -47
- package/dist/file-lock.js +112 -0
- package/dist/harness-codex.js +35 -29
- package/dist/harness-links.js +0 -3
- package/dist/harness-pi.js +57 -0
- package/dist/harness.js +10 -15
- package/dist/help.js +16 -18
- package/dist/index.js +11 -5
- package/dist/list.js +3 -47
- package/dist/model.js +4 -6
- package/dist/notifications.js +217 -32
- package/dist/paseo-connection.js +135 -0
- package/dist/paseo-delivery.js +73 -144
- package/dist/paseo-state.js +1 -1
- package/dist/paseo-timeline.js +32 -42
- package/dist/presentation.js +24 -39
- package/dist/presented.js +10 -72
- package/dist/registry.js +23 -24
- package/dist/routes.js +153 -0
- package/dist/runtime.js +6 -21
- package/dist/square-application.js +56 -127
- package/dist/square-core.js +56 -9
- package/dist/stream.js +1 -1
- package/dist/wake-attempts.js +175 -0
- package/dist/wake-evidence.js +35 -0
- package/dist/wake-port.js +22 -0
- package/dist/wake-sink.js +45 -6
- package/dist/watch.js +1 -2
- package/guides/participant.md +7 -174
- package/package.json +6 -3
- package/skills/brainstorm/SKILL.md +28 -28
- package/skills/square/.claude-plugin/plugin.json +1 -1
- package/skills/square/SKILL.md +23 -14
- package/skills/square-feedback/SKILL.md +7 -7
- package/dist/doctor.js +0 -35
- package/dist/notification-failures.js +0 -54
- package/template.md +0 -4
- package/templates/architect.md +0 -4
- package/templates/brainstorm.md +0 -4
|
@@ -16,7 +16,7 @@ export function pendingAtBoundary(inbox) {
|
|
|
16
16
|
return membership;
|
|
17
17
|
return {
|
|
18
18
|
...membership,
|
|
19
|
-
notifications: membership.notifications.filter((notification) => !leaseOwnsNotification(lease,
|
|
19
|
+
notifications: membership.notifications.filter((notification) => !leaseOwnsNotification(lease, notification)),
|
|
20
20
|
};
|
|
21
21
|
})
|
|
22
22
|
.filter((membership) => membership.notifications.length > 0);
|
package/dist/cli/context.js
CHANGED
|
@@ -4,7 +4,7 @@ import path from 'node:path';
|
|
|
4
4
|
import { loadSquare } from '../artifact.js';
|
|
5
5
|
import { commandUsageHint } from '../help.js';
|
|
6
6
|
import { parseParticipantList, validateName } from '../model.js';
|
|
7
|
-
export const DEFAULT_SQUARE_PATH = '.square/SQUARE.
|
|
7
|
+
export const DEFAULT_SQUARE_PATH = '.square/SQUARE.square';
|
|
8
8
|
export function readStdinSync() {
|
|
9
9
|
try {
|
|
10
10
|
return fs.readFileSync(0, 'utf8');
|
|
@@ -99,7 +99,7 @@ function resolveDefaultSquarePath() {
|
|
|
99
99
|
}
|
|
100
100
|
const candidates = [];
|
|
101
101
|
for (const entry of entries) {
|
|
102
|
-
if (!entry.isFile() || !entry.name.endsWith('.
|
|
102
|
+
if (!entry.isFile() || !entry.name.endsWith('.square'))
|
|
103
103
|
continue;
|
|
104
104
|
const fullPath = path.join(directory, entry.name);
|
|
105
105
|
try {
|
|
@@ -118,7 +118,7 @@ export function parseGlobalArgs(rawArgs) {
|
|
|
118
118
|
let requestedPath;
|
|
119
119
|
let name;
|
|
120
120
|
for (let index = 0; index < args.length; index++) {
|
|
121
|
-
if (args[index] === '--
|
|
121
|
+
if (args[index] === '--location') {
|
|
122
122
|
requestedPath = requireValue(args, index, args[index]);
|
|
123
123
|
args.splice(index, 2);
|
|
124
124
|
index -= 1;
|
|
@@ -87,7 +87,7 @@ export function formatHarnessResult(result) {
|
|
|
87
87
|
return `${[...result.notes, ...result.lines].join('\n')}\n`;
|
|
88
88
|
}
|
|
89
89
|
export function runHarnessCommand(argv, squarePath) {
|
|
90
|
-
const context = { homeDir: os.homedir(), squarePath: squarePath ?? '.square/SQUARE.
|
|
90
|
+
const context = { homeDir: os.homedir(), squarePath: squarePath ?? '.square/SQUARE.square', command: 'harness' };
|
|
91
91
|
const intent = harnessCommand.parse(argv, context);
|
|
92
92
|
return Promise.resolve(harnessCommand.execute(intent, context)).then(formatHarnessResult);
|
|
93
93
|
}
|
|
@@ -1,71 +1,25 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import { renderDoctorClean, renderDoctorProblems, renderDoctorRepaired, renderDoctorUnfixable, withPathOutput, } from '../presentation.js';
|
|
1
|
+
import { diagnoseSquareFile } from '../artifact.js';
|
|
2
|
+
import { renderDoctorClean, renderDoctorUnfixable, withPathOutput } from '../presentation.js';
|
|
4
3
|
import { inSquareCount } from '../runtime.js';
|
|
5
|
-
import { repairSquare } from '../square-application.js';
|
|
6
|
-
import { SquareError } from '../model.js';
|
|
7
|
-
import { pruneRegistry } from '../registry.js';
|
|
8
4
|
import { usage } from './context.js';
|
|
9
|
-
function readSquareText(squarePath) {
|
|
10
|
-
try {
|
|
11
|
-
return fs.readFileSync(squarePath, 'utf8');
|
|
12
|
-
}
|
|
13
|
-
catch (error) {
|
|
14
|
-
if (error.code === 'ENOENT')
|
|
15
|
-
throw new SquareError('not_found', `square file not found: ${squarePath}`);
|
|
16
|
-
throw error;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
function quarantinePath(squarePath) {
|
|
20
|
-
return squarePath.replace(/\.md$/, '') + '.quarantine.md';
|
|
21
|
-
}
|
|
22
5
|
export const doctorCommand = {
|
|
23
6
|
parse(argv, context) {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if (argument === '--fix')
|
|
28
|
-
fix = true;
|
|
29
|
-
else if (argument === '--before') {
|
|
30
|
-
index += 1;
|
|
31
|
-
if (argv[index] === undefined)
|
|
32
|
-
usage(context.command);
|
|
33
|
-
}
|
|
34
|
-
else
|
|
35
|
-
usage(context.command);
|
|
36
|
-
}
|
|
37
|
-
return { fix };
|
|
7
|
+
if (argv.length > 0)
|
|
8
|
+
usage(context.command);
|
|
9
|
+
return undefined;
|
|
38
10
|
},
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if (diagnosis.unfixable) {
|
|
43
|
-
return {
|
|
44
|
-
output: withPathOutput(context.squarePath, renderDoctorUnfixable(diagnosis.unfixable), { participantCount: inSquareCount(loadSquare(context.squarePath)) }),
|
|
45
|
-
exitCode: 2,
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
const summary = diagnosis.problems.length === 0 ? renderDoctorClean() : renderDoctorProblems(diagnosis.problems);
|
|
11
|
+
execute(_intent, context) {
|
|
12
|
+
const diagnosis = diagnoseSquareFile(context.squarePath);
|
|
13
|
+
if (diagnosis.unfixable !== undefined || diagnosis.doc === undefined) {
|
|
49
14
|
return {
|
|
50
|
-
output: withPathOutput(context.squarePath,
|
|
51
|
-
exitCode: diagnosis.problems.length === 0 ? 0 : 1,
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
const repair = await repairSquare(context.squarePath);
|
|
55
|
-
if (repair.diagnosis.unfixable) {
|
|
56
|
-
return {
|
|
57
|
-
output: withPathOutput(context.squarePath, renderDoctorUnfixable(repair.diagnosis.unfixable), { participantCount: inSquareCount(loadSquare(context.squarePath)) }),
|
|
15
|
+
output: withPathOutput(context.squarePath, renderDoctorUnfixable(diagnosis.unfixable ?? 'the snapshot could not be decoded')),
|
|
58
16
|
exitCode: 2,
|
|
59
17
|
};
|
|
60
18
|
}
|
|
61
|
-
const repaired = repair.repaired;
|
|
62
|
-
const registry = pruneRegistry();
|
|
63
|
-
if (registry.removed > 0) {
|
|
64
|
-
repaired.actions.push({ message: `pruned ${registry.removed} obsolete registry membership(s)` });
|
|
65
|
-
}
|
|
66
|
-
const sidecar = quarantinePath(context.squarePath);
|
|
67
19
|
return {
|
|
68
|
-
output: withPathOutput(context.squarePath,
|
|
20
|
+
output: withPathOutput(context.squarePath, renderDoctorClean(), {
|
|
21
|
+
participantCount: inSquareCount(diagnosis.doc),
|
|
22
|
+
}),
|
|
69
23
|
};
|
|
70
24
|
},
|
|
71
25
|
present(result) {
|
|
@@ -3,11 +3,12 @@ import { runClaudeHook } from '../claude-hook.js';
|
|
|
3
3
|
import { runCodexHook } from '../codex-hook.js';
|
|
4
4
|
import { coreActivities, coreParticipants, coreStatus } from '../decisions.js';
|
|
5
5
|
import { sessionInbox } from '../inbox.js';
|
|
6
|
+
import { sweepPendingNotifications } from '../notifications.js';
|
|
6
7
|
import { cmdListSquares } from '../list.js';
|
|
7
8
|
import { sameName } from '../model.js';
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import { actId, inSquareCount,
|
|
9
|
+
import { parseActivityId } from '../square-core.js';
|
|
10
|
+
import { commandPrefix, participantCommandPrefix, renderActivitiesView, renderGrepActivitiesView, renderAmbientEvent, withPathOutput, } from '../presentation.js';
|
|
11
|
+
import { actId, inSquareCount, nowMs, sayNumberFor } from '../runtime.js';
|
|
11
12
|
import { cmdStream, cmdStreamNdjson } from '../stream.js';
|
|
12
13
|
import { formatRelativeTime, formatTimestamp, parseTimeOrRelative } from '../time.js';
|
|
13
14
|
import { cmdWatch } from '../watch.js';
|
|
@@ -93,14 +94,15 @@ export const catchCommand = {
|
|
|
93
94
|
},
|
|
94
95
|
async execute(intent, context) {
|
|
95
96
|
await cmdWatch(context.squarePath, requireParticipant(context.name), intent);
|
|
97
|
+
await sweepPendingNotifications(context.squarePath);
|
|
96
98
|
},
|
|
97
99
|
present: () => { },
|
|
98
100
|
};
|
|
99
101
|
function parseActRef(value, flag) {
|
|
100
|
-
const
|
|
101
|
-
if (
|
|
102
|
-
fail(`Invalid ${flag}: expected an activity id like
|
|
103
|
-
return
|
|
102
|
+
const index = parseActivityId(value);
|
|
103
|
+
if (index === undefined)
|
|
104
|
+
fail(`Invalid ${flag}: expected an activity id like act/12`);
|
|
105
|
+
return index;
|
|
104
106
|
}
|
|
105
107
|
function parseTimestamp(value, flag) {
|
|
106
108
|
const timestamp = parseTimeOrRelative(value, nowMs());
|
|
@@ -304,24 +306,16 @@ export const historyCommand = {
|
|
|
304
306
|
return events.map((item) => renderFields(doc, item, options.format)).join('\n') + (events.length > 0 ? '\n' : '');
|
|
305
307
|
}
|
|
306
308
|
const pattern = options.grep ?? options.fixed;
|
|
309
|
+
const archive = options.atIndex != null
|
|
310
|
+
|| (options.ids !== undefined && options.ids.length > 0)
|
|
311
|
+
|| (options.lastN == null && options.full === true);
|
|
307
312
|
const output = pattern === undefined || pattern === ''
|
|
308
|
-
? renderActivitiesView(doc, events, null, options.full, context.squarePath, options.viewer ?? '')
|
|
313
|
+
? renderActivitiesView(doc, events, null, options.full, context.squarePath, options.viewer ?? '', archive ? 'archive' : 'ambient')
|
|
309
314
|
: renderGrepActivitiesView(events, totalMatches, options.full, context.squarePath, pattern, options.fixed !== undefined);
|
|
310
315
|
return withPathOutput(context.squarePath, output, { participantCount: inSquareCount(doc) });
|
|
311
316
|
},
|
|
312
317
|
present: (result) => process.stdout.write(result),
|
|
313
318
|
};
|
|
314
|
-
export const warmupCommand = {
|
|
315
|
-
parse(argv, context) { if (argv.length > 0)
|
|
316
|
-
usage(context.command); return undefined; },
|
|
317
|
-
execute(_intent, context) {
|
|
318
|
-
const doc = loadSquare(context.squarePath);
|
|
319
|
-
return withPathOutput(context.squarePath, doc.warmup.join('\n'), {
|
|
320
|
-
participantCount: inSquareCount(doc),
|
|
321
|
-
});
|
|
322
|
-
},
|
|
323
|
-
present: (result) => process.stdout.write(result),
|
|
324
|
-
};
|
|
325
319
|
export const participantsCommand = {
|
|
326
320
|
parse(argv, context) { if (argv.length > 0)
|
|
327
321
|
usage(context.command); return undefined; },
|
|
@@ -382,7 +376,7 @@ export const statusCommand = {
|
|
|
382
376
|
: undefined;
|
|
383
377
|
const visible = result.latestAct === undefined
|
|
384
378
|
? ''
|
|
385
|
-
:
|
|
379
|
+
: renderAmbientEvent(result.latestAct, context.name ?? '', {
|
|
386
380
|
now: result.now,
|
|
387
381
|
preview: 200,
|
|
388
382
|
actNumber: result.latestAct.kind === 'say'
|
|
@@ -440,17 +434,3 @@ function hookCommand(runHook) {
|
|
|
440
434
|
}
|
|
441
435
|
export const claudeHookCommand = hookCommand(runClaudeHook);
|
|
442
436
|
export const codexHookCommand = hookCommand(runCodexHook);
|
|
443
|
-
/** Maintain the local discovery cache before participant-facing adapters run. */
|
|
444
|
-
export function refreshLocalRegistration(squarePath, name) {
|
|
445
|
-
if (name === undefined)
|
|
446
|
-
return;
|
|
447
|
-
try {
|
|
448
|
-
const doc = loadSquare(squarePath);
|
|
449
|
-
const known = resolveRosterName(doc, name);
|
|
450
|
-
if (known !== undefined && isCurrentlyJoined(doc.acts, known))
|
|
451
|
-
recordLocalJoin(known, squarePath);
|
|
452
|
-
}
|
|
453
|
-
catch {
|
|
454
|
-
// The machine-local discovery cache never makes a Square command fail.
|
|
455
|
-
}
|
|
456
|
-
}
|
package/dist/cli/program.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { helpRequest } from '../help.js';
|
|
2
2
|
import { SquareError } from '../model.js';
|
|
3
3
|
import { defaultContext, parseGlobalArgs } from './context.js';
|
|
4
|
-
import { refreshLocalRegistration } from './observation-commands.js';
|
|
5
4
|
import { executeRegisteredCommand, findCommand } from './registry.js';
|
|
6
5
|
function isMutatingCommand(command, argv) {
|
|
6
|
+
void argv;
|
|
7
7
|
if (['build', 'join', 'catch', 'express', 'done', 'hold', 'resume', 'compact'].includes(command))
|
|
8
8
|
return true;
|
|
9
|
-
return
|
|
9
|
+
return false;
|
|
10
10
|
}
|
|
11
11
|
function handleSquareError(error) {
|
|
12
12
|
if (error instanceof SquareError) {
|
|
@@ -20,7 +20,7 @@ export async function runCli(rawArgs = process.argv.slice(2)) {
|
|
|
20
20
|
try {
|
|
21
21
|
const requestedHelp = helpRequest(rawArgs);
|
|
22
22
|
if (requestedHelp !== undefined) {
|
|
23
|
-
await executeRegisteredCommand('help', requestedHelp.command === undefined ? [] : [requestedHelp.command], defaultContext('help', '.square/SQUARE.
|
|
23
|
+
await executeRegisteredCommand('help', requestedHelp.command === undefined ? [] : [requestedHelp.command], defaultContext('help', '.square/SQUARE.square'));
|
|
24
24
|
return;
|
|
25
25
|
}
|
|
26
26
|
const parsed = parseGlobalArgs(rawArgs);
|
|
@@ -37,9 +37,6 @@ export async function runCli(rawArgs = process.argv.slice(2)) {
|
|
|
37
37
|
process.stderr.write('✕ more than one square is active here; choose the path before changing or consuming activity.\n» square list\n');
|
|
38
38
|
process.exit(2);
|
|
39
39
|
}
|
|
40
|
-
if (['express', 'catch', 'done', 'hold', 'resume'].includes(command)) {
|
|
41
|
-
refreshLocalRegistration(parsed.squarePath, parsed.name);
|
|
42
|
-
}
|
|
43
40
|
await executeRegisteredCommand(command, parsed.args.slice(1), defaultContext(command, parsed.squarePath, parsed.name));
|
|
44
41
|
}
|
|
45
42
|
catch (error) {
|
package/dist/cli/registry.js
CHANGED
|
@@ -2,7 +2,7 @@ import { buildCommand, compactCommand, doneCommand, expressCommand, holdCommand,
|
|
|
2
2
|
import { doctorCommand } from './maintenance-commands.js';
|
|
3
3
|
import { harnessCommand, installCommand, uninstallCommand } from './harness-command.js';
|
|
4
4
|
import { helpCommand, versionCommand } from './meta-commands.js';
|
|
5
|
-
import { catchCommand, claudeHookCommand, codexHookCommand, historyCommand, inboxCommand, listCommand, participantsCommand, statusCommand, streamCommand,
|
|
5
|
+
import { catchCommand, claudeHookCommand, codexHookCommand, historyCommand, inboxCommand, listCommand, participantsCommand, statusCommand, streamCommand, } from './observation-commands.js';
|
|
6
6
|
/** Every public command is an executable adapter, including aliases and utility commands. */
|
|
7
7
|
export const commandRegistry = [
|
|
8
8
|
{ names: ['build'], spec: buildCommand },
|
|
@@ -23,7 +23,6 @@ export const commandRegistry = [
|
|
|
23
23
|
{ names: ['compact'], spec: compactCommand },
|
|
24
24
|
{ names: ['doctor'], spec: doctorCommand },
|
|
25
25
|
{ names: ['history'], spec: historyCommand },
|
|
26
|
-
{ names: ['warmup'], spec: warmupCommand },
|
|
27
26
|
{ names: ['status'], spec: statusCommand },
|
|
28
27
|
{ names: ['participants'], spec: participantsCommand },
|
|
29
28
|
{ names: ['help'], spec: helpCommand },
|
|
@@ -2,10 +2,12 @@ import { cmdActivity } from '../activity.js';
|
|
|
2
2
|
import { loadSquare } from '../artifact.js';
|
|
3
3
|
import { cmdCompact } from '../compact.js';
|
|
4
4
|
import { SquareError, formatHardCap, validateName, } from '../model.js';
|
|
5
|
-
import { participantCommandPrefix,
|
|
6
|
-
import { hasAutomaticDeliveryIdentity, recordLocalDone, recordLocalJoin } from '../registry.js';
|
|
5
|
+
import { participantCommandPrefix, renderEventCli, renderPublicTail, withPathOutput, } from '../presentation.js';
|
|
6
|
+
import { hasAutomaticDeliveryIdentity, localParticipantOwner, recordLocalDone, recordLocalJoin } from '../registry.js';
|
|
7
|
+
import { sweepPendingNotifications } from '../notifications.js';
|
|
7
8
|
import { inSquareCount, isCurrentlyJoined, nowMs, resolveRosterName } from '../runtime.js';
|
|
8
9
|
import { createSquare, execute } from '../square-application.js';
|
|
10
|
+
import { formatActivityId, parseActivityId } from '../square-core.js';
|
|
9
11
|
import { fail, parseHardCap, parsePositiveInteger, readPipedBodyFallback, readStdinSync, requireParticipant, requireValue, resolveBody, usage, } from './context.js';
|
|
10
12
|
function parseBuild(argv) {
|
|
11
13
|
const options = { force: false, hardCap: null };
|
|
@@ -56,6 +58,7 @@ export const buildCommand = {
|
|
|
56
58
|
};
|
|
57
59
|
function parseJoin(argv, context) {
|
|
58
60
|
let lastN = 10;
|
|
61
|
+
let kick = false;
|
|
59
62
|
for (let index = 0; index < argv.length; index++) {
|
|
60
63
|
if (argv[index] === '--last') {
|
|
61
64
|
lastN = parsePositiveInteger(requireValue(argv, index, argv[index]), argv[index]);
|
|
@@ -64,11 +67,14 @@ function parseJoin(argv, context) {
|
|
|
64
67
|
else if (argv[index] === '--all') {
|
|
65
68
|
lastN = null;
|
|
66
69
|
}
|
|
70
|
+
else if (argv[index] === '--kick') {
|
|
71
|
+
kick = true;
|
|
72
|
+
}
|
|
67
73
|
else {
|
|
68
74
|
usage(context.command);
|
|
69
75
|
}
|
|
70
76
|
}
|
|
71
|
-
return { name: requireParticipant(context.name), lastN };
|
|
77
|
+
return { name: requireParticipant(context.name), lastN, kick };
|
|
72
78
|
}
|
|
73
79
|
export const joinCommand = {
|
|
74
80
|
parse: parseJoin,
|
|
@@ -81,16 +87,18 @@ export const joinCommand = {
|
|
|
81
87
|
const after = loadSquare(context.squarePath);
|
|
82
88
|
const preamble = after.preamble.at(-1) === '---' ? after.preamble.slice(0, -1) : after.preamble;
|
|
83
89
|
recordLocalJoin(joinedName, context.squarePath);
|
|
90
|
+
await sweepPendingNotifications(context.squarePath);
|
|
84
91
|
const activities = renderPublicTail(after.acts, intent.lastN, nowMs(), joinedName);
|
|
85
92
|
const contextText = preamble.join('\n').trim();
|
|
86
93
|
const fallback = hasAutomaticDeliveryIdentity()
|
|
87
94
|
? []
|
|
88
95
|
: ['', `» ${participantCommandPrefix(context.squarePath, joinedName)} catch --idle 30m`, ' no session delivery detected — keep this catch open for new activity'];
|
|
96
|
+
const scene = after.warmup.join('\n').trim();
|
|
89
97
|
const output = [
|
|
90
|
-
`●
|
|
98
|
+
`● You stepped into the square`,
|
|
99
|
+
...(isRejoin || scene === '' ? [] : ['', scene]),
|
|
91
100
|
...(isRejoin || contextText === '' ? [] : ['', 'context', contextText]),
|
|
92
101
|
...(activities === '' ? [] : ['', 'recent activity', activities]),
|
|
93
|
-
...(isRejoin ? [] : ['', `» ${participantCommandPrefix(context.squarePath, joinedName)} warmup`]),
|
|
94
102
|
...fallback,
|
|
95
103
|
].join('\n');
|
|
96
104
|
return withPathOutput(context.squarePath, output, { participantCount: inSquareCount(after) });
|
|
@@ -102,11 +110,26 @@ export const joinCommand = {
|
|
|
102
110
|
const joinedName = resolveRosterName(doc, intent.name);
|
|
103
111
|
if (joinedName === undefined || !isCurrentlyJoined(doc.acts, joinedName))
|
|
104
112
|
throw error;
|
|
113
|
+
const reconnect = localParticipantOwner(context.squarePath, joinedName) !== undefined;
|
|
114
|
+
if (!intent.kick && !reconnect) {
|
|
115
|
+
fail([
|
|
116
|
+
`✕ ${joinedName} shoos you out of the square`,
|
|
117
|
+
` · a same-named participant stands here — the name is taken`,
|
|
118
|
+
` · --kick banishes her and the name becomes yours`,
|
|
119
|
+
`» ${participantCommandPrefix(context.squarePath, joinedName)} join --kick`,
|
|
120
|
+
].join('\n'));
|
|
121
|
+
}
|
|
105
122
|
recordLocalJoin(joinedName, context.squarePath);
|
|
123
|
+
await sweepPendingNotifications(context.squarePath);
|
|
106
124
|
const fallback = hasAutomaticDeliveryIdentity()
|
|
107
125
|
? ''
|
|
108
126
|
: `\n» ${participantCommandPrefix(context.squarePath, joinedName)} catch --idle 30m\n no session delivery detected — keep this catch open for new activity`;
|
|
109
|
-
|
|
127
|
+
const line = reconnect && !intent.kick
|
|
128
|
+
? `● you are already in the square`
|
|
129
|
+
: `✓ you banished the original ${joinedName} — the name is yours`;
|
|
130
|
+
const scene = doc.warmup.join('\n').trim();
|
|
131
|
+
const showScene = !(reconnect && !intent.kick) && scene !== '';
|
|
132
|
+
return withPathOutput(context.squarePath, `${line}${showScene ? `\n\n${scene}` : ''}${fallback}`, { participantCount: inSquareCount(doc) });
|
|
110
133
|
}
|
|
111
134
|
},
|
|
112
135
|
present: (result) => process.stdout.write(result),
|
|
@@ -114,7 +137,6 @@ export const joinCommand = {
|
|
|
114
137
|
function parseActivity(argv, context) {
|
|
115
138
|
let force = false;
|
|
116
139
|
let noWait = false;
|
|
117
|
-
let beside;
|
|
118
140
|
let bell = false;
|
|
119
141
|
let reply;
|
|
120
142
|
const bodyArgs = [];
|
|
@@ -124,25 +146,21 @@ function parseActivity(argv, context) {
|
|
|
124
146
|
force = true;
|
|
125
147
|
else if (argument === '--no-wait')
|
|
126
148
|
noWait = true;
|
|
127
|
-
else if (argument === '--beside')
|
|
128
|
-
beside
|
|
129
|
-
index += 1;
|
|
130
|
-
}
|
|
149
|
+
else if (argument === '--beside')
|
|
150
|
+
fail('✕ express does not know --beside\n» square express --help');
|
|
131
151
|
else if (argument === '--bell')
|
|
132
152
|
bell = true;
|
|
133
153
|
else if (argument === '--reply') {
|
|
134
|
-
const
|
|
135
|
-
if (
|
|
136
|
-
fail('Invalid --reply: expected an activity id like
|
|
137
|
-
reply =
|
|
154
|
+
const replyIndex = parseActivityId(requireValue(argv, index, argument));
|
|
155
|
+
if (replyIndex === undefined)
|
|
156
|
+
fail('Invalid --reply: expected an activity id like act/12');
|
|
157
|
+
reply = replyIndex;
|
|
138
158
|
index += 1;
|
|
139
159
|
}
|
|
140
160
|
else
|
|
141
161
|
bodyArgs.push(argument);
|
|
142
162
|
}
|
|
143
|
-
|
|
144
|
-
fail('Invalid express options: --beside and --bell are mutually exclusive.');
|
|
145
|
-
const reach = bell ? 'bell' : beside === undefined ? undefined : { beside };
|
|
163
|
+
const reach = bell ? 'bell' : undefined;
|
|
146
164
|
if (bodyArgs.length !== 1) {
|
|
147
165
|
if (bodyArgs.length === 0) {
|
|
148
166
|
const piped = readPipedBodyFallback();
|
|
@@ -156,13 +174,14 @@ function parseActivity(argv, context) {
|
|
|
156
174
|
export const expressCommand = {
|
|
157
175
|
parse: parseActivity,
|
|
158
176
|
async execute(intent, context) {
|
|
159
|
-
|
|
177
|
+
await sweepPendingNotifications(context.squarePath);
|
|
178
|
+
const reachArg = intent.reach === 'bell' ? ' --bell' : '';
|
|
160
179
|
await cmdActivity(context.squarePath, intent.name, intent.activity, resolveBody, {
|
|
161
180
|
force: intent.force,
|
|
162
181
|
noWait: intent.noWait,
|
|
163
182
|
reach: intent.reach,
|
|
164
183
|
reply: intent.reply,
|
|
165
|
-
forceCommand: `${participantCommandPrefix(context.squarePath, intent.name)} express --force${reachArg}${intent.reply === undefined ? '' : ` --reply
|
|
184
|
+
forceCommand: `${participantCommandPrefix(context.squarePath, intent.name)} express --force${reachArg}${intent.reply === undefined ? '' : ` --reply ${formatActivityId(intent.reply)}`} -`,
|
|
166
185
|
});
|
|
167
186
|
},
|
|
168
187
|
present: () => { },
|
package/dist/cmd/notify-once.js
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { resolve } from 'node:path';
|
|
3
3
|
import { setTimeout as sleep } from 'node:timers/promises';
|
|
4
|
-
import {
|
|
5
|
-
import { notificationDeliveryWaitMs, processActNotificationsOnce } from '../notifications.js';
|
|
4
|
+
import { processActNotificationsOnce, wakeGraceMs } from '../notifications.js';
|
|
6
5
|
function args(argv) {
|
|
7
6
|
let squarePath;
|
|
8
7
|
let actIndex;
|
|
9
8
|
for (let index = 0; index < argv.length; index += 1) {
|
|
10
|
-
if (argv[index] === '--
|
|
9
|
+
if (argv[index] === '--location' && argv[index + 1] !== undefined)
|
|
11
10
|
squarePath = resolve(argv[++index]);
|
|
12
11
|
else if (argv[index] === '--act-index' && /^\d+$/.test(argv[index + 1] ?? ''))
|
|
13
12
|
actIndex = Number(argv[++index]);
|
|
@@ -15,25 +14,16 @@ function args(argv) {
|
|
|
15
14
|
throw new Error(`Unknown notify-once argument: ${argv[index]}`);
|
|
16
15
|
}
|
|
17
16
|
if (squarePath === undefined || actIndex === undefined)
|
|
18
|
-
throw new Error('notify-once requires --
|
|
17
|
+
throw new Error('notify-once requires --location and --act-index.');
|
|
19
18
|
return { squarePath, actIndex };
|
|
20
19
|
}
|
|
21
20
|
async function main() {
|
|
22
21
|
if (process.env.SQUARE_DISABLE_PASEO_WAKE === '1')
|
|
23
22
|
return;
|
|
24
23
|
const { squarePath, actIndex } = args(process.argv.slice(2));
|
|
25
|
-
await sleep(
|
|
24
|
+
await sleep(wakeGraceMs());
|
|
26
25
|
await processActNotificationsOnce(squarePath, actIndex);
|
|
27
26
|
}
|
|
28
|
-
main().catch((
|
|
29
|
-
const squarePath = process.argv.includes('--square-path') ? process.argv[process.argv.indexOf('--square-path') + 1] : undefined;
|
|
30
|
-
if (squarePath) {
|
|
31
|
-
recordNotificationFailure(squarePath, {
|
|
32
|
-
actIndex: Number(process.argv[process.argv.indexOf('--act-index') + 1]) || 0,
|
|
33
|
-
sink: 'worker',
|
|
34
|
-
message: error instanceof Error ? error.message : String(error),
|
|
35
|
-
diagnostic: { phase: 'worker' },
|
|
36
|
-
});
|
|
37
|
-
}
|
|
27
|
+
main().catch(() => {
|
|
38
28
|
process.exitCode = 0;
|
|
39
29
|
});
|
package/dist/compact.js
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { SquareError } from './model.js';
|
|
2
2
|
import { withPathOutput } from './presentation.js';
|
|
3
3
|
import { execute } from './square-application.js';
|
|
4
|
-
function
|
|
5
|
-
return squarePath.replace(/\.
|
|
4
|
+
function archivePath(squarePath) {
|
|
5
|
+
return squarePath.replace(/\.square$/, '') + '.archive.square';
|
|
6
6
|
}
|
|
7
7
|
export async function cmdCompact(squarePath, opts) {
|
|
8
8
|
try {
|
|
9
9
|
let archivedCount;
|
|
10
10
|
let keptCount;
|
|
11
|
-
const archive =
|
|
11
|
+
const archive = archivePath(squarePath);
|
|
12
12
|
const committed = await execute(squarePath, { type: 'compact', keep: opts.keep, archivePath: archive });
|
|
13
13
|
const result = committed.result;
|
|
14
14
|
archivedCount = result.archived.length;
|
|
15
15
|
keptCount = result.doc.acts.length;
|
|
16
|
-
const summary = ['✓ compacted', ` · archived ${archivedCount} activities`, ` · kept ${keptCount} activities`, ...(archivedCount > 0 ? [` ·
|
|
16
|
+
const summary = ['✓ compacted', ` · archived ${archivedCount} activities`, ` · kept ${keptCount} activities`, ...(archivedCount > 0 ? [` · archive ${archive}`] : [])].join('\n');
|
|
17
17
|
process.stdout.write(withPathOutput(squarePath, summary));
|
|
18
18
|
}
|
|
19
19
|
catch (err) {
|
package/dist/decisions.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { SquareError, sameName, validateName, } from './model.js';
|
|
2
2
|
import { UNREAD_BLOCK_GRACE_MS, actId, actStableIndex, foldedState, freshWatchLease, getReadState, publicActs, readCursor, resolveRosterName, rosterNames, matchesMentionTarget, THROTTLE_WINDOW_MS, } from './runtime.js';
|
|
3
3
|
import { actDelta, peerPublicActs, peerRoomChanges } from './activity-feed.js';
|
|
4
|
-
import { validate } from './square-core.js';
|
|
4
|
+
import { extractMentions, formatActivityId, validate } from './square-core.js';
|
|
5
5
|
import { deriveDeliveryModel } from './delivery.js';
|
|
6
6
|
import { compileSearchPattern } from './search.js';
|
|
7
7
|
export function resolveKnownName(doc, name) {
|
|
@@ -27,7 +27,7 @@ export function decideJoin(doc, name, now) {
|
|
|
27
27
|
return {
|
|
28
28
|
joinedName,
|
|
29
29
|
addParticipant: knownName === undefined,
|
|
30
|
-
joinAct: { kind: 'join', actor: joinedName, at: now
|
|
30
|
+
joinAct: { kind: 'join', actor: joinedName, at: now },
|
|
31
31
|
};
|
|
32
32
|
}
|
|
33
33
|
const UNREAD_PREVIEW_LIMIT = 3;
|
|
@@ -41,12 +41,21 @@ export function decideAct(doc, input) {
|
|
|
41
41
|
const reply = input.reply;
|
|
42
42
|
if (reply !== undefined) {
|
|
43
43
|
if (!Number.isSafeInteger(reply) || reply < 0 || reply >= doc.runtime.nextActIndex) {
|
|
44
|
-
|
|
44
|
+
const label = Number.isSafeInteger(reply) && reply >= 0 ? formatActivityId(reply) : String(reply);
|
|
45
|
+
throw new SquareError('invalid_args', `Unknown reply activity: ${label}`);
|
|
45
46
|
}
|
|
46
47
|
}
|
|
47
48
|
const state = foldedState(doc);
|
|
49
|
+
if (reach !== 'bell'
|
|
50
|
+
&& extractMentions(body).length === 0) {
|
|
51
|
+
throw new SquareError('invalid_args', 'express requires an @mention unless using --bell');
|
|
52
|
+
}
|
|
48
53
|
const current = participantState(state, name);
|
|
49
|
-
const result = validate(state, {
|
|
54
|
+
const result = validate(state, {
|
|
55
|
+
kind: 'say', actor: name, at: now, body,
|
|
56
|
+
...(reach !== undefined ? { reach } : {}),
|
|
57
|
+
...(reply !== undefined ? { reply } : {}),
|
|
58
|
+
}, { hardCap: doc.hardCap, throttlePerMinute: doc.throttlePerMinute, throttleWindowMs: THROTTLE_WINDOW_MS });
|
|
50
59
|
if (!result.ok) {
|
|
51
60
|
if (result.reason === 'done')
|
|
52
61
|
throw new SquareError('conflict', `${name} is done; rejoin to express again`);
|
|
@@ -93,7 +102,8 @@ export function decideAct(doc, input) {
|
|
|
93
102
|
}))
|
|
94
103
|
.sort((a, b) => a.latestActivityAgeMs - b.latestActivityAgeMs || a.name.localeCompare(b.name));
|
|
95
104
|
const latestActivityAgeMs = activitySummaries[0]?.latestActivityAgeMs;
|
|
96
|
-
const
|
|
105
|
+
const hasUnreadBlockingChange = unreadRoomChanges.some((act) => act.kind !== 'join');
|
|
106
|
+
const hasUnread = unreadPublic.length > 0 || hasUnreadBlockingChange;
|
|
97
107
|
const hasFreshUnreadActivity = latestActivityAgeMs !== undefined && latestActivityAgeMs <= UNREAD_BLOCK_GRACE_MS;
|
|
98
108
|
if (!force && hasUnread && !hasFreshUnreadActivity) {
|
|
99
109
|
return { type: 'blocked', activitySummaries, unreadRoomChanges };
|
|
@@ -101,7 +111,11 @@ export function decideAct(doc, input) {
|
|
|
101
111
|
const ownActCount = (current?.activityCount ?? 0) + 1;
|
|
102
112
|
return {
|
|
103
113
|
type: 'sent',
|
|
104
|
-
act: {
|
|
114
|
+
act: {
|
|
115
|
+
kind: 'say', actor: name, at: now, body,
|
|
116
|
+
...(reach !== undefined ? { reach } : {}),
|
|
117
|
+
...(reply !== undefined ? { reply } : {}),
|
|
118
|
+
},
|
|
105
119
|
confirmation: `● heads turn your way — #${ownActCount}`,
|
|
106
120
|
ownActCount,
|
|
107
121
|
pendingPublic: unreadPublic,
|
|
@@ -116,7 +130,7 @@ export function coreHold(_doc, actor, body, now) {
|
|
|
116
130
|
return { kind: 'hold', actor, at: now, body: body.replace(/\r\n/g, '\n').trim() };
|
|
117
131
|
}
|
|
118
132
|
export function coreResume(_doc, actor, now) {
|
|
119
|
-
return { kind: 'resume', actor, at: now
|
|
133
|
+
return { kind: 'resume', actor, at: now };
|
|
120
134
|
}
|
|
121
135
|
function presenceFor(doc, snapshot, name, now) {
|
|
122
136
|
if (snapshot?.done)
|