@astrosheep/square 0.3.34 → 0.3.36
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/claude-plugin/.claude-plugin/plugin.json +1 -1
- package/claude-plugin/skills/square/SKILL.md +10 -10
- package/codex-plugin/.codex-plugin/plugin.json +1 -1
- package/codex-plugin/hooks/hooks.json +1 -1
- package/dist/activity-feed.js +1 -1
- package/dist/activity.d.ts +1 -0
- package/dist/activity.js +6 -3
- package/dist/artifact.js +2 -1
- package/dist/automatic-session.js +17 -1
- package/dist/boundary-presentation.d.ts +1 -0
- package/dist/boundary-presentation.js +12 -1
- package/dist/catch-decisions.d.ts +4 -0
- package/dist/catch-decisions.js +23 -7
- package/dist/cli/context.d.ts +1 -0
- package/dist/cli/context.js +26 -1
- package/dist/cli/observation-commands.d.ts +7 -1
- package/dist/cli/observation-commands.js +149 -63
- package/dist/cli/program.js +8 -2
- package/dist/cli/square-commands.d.ts +3 -1
- package/dist/cli/square-commands.js +42 -16
- package/dist/codex-queue.d.ts +1 -0
- package/dist/codex-queue.js +1 -1
- package/dist/decisions.d.ts +1 -0
- package/dist/decisions.js +29 -14
- package/dist/delivery-health.d.ts +1 -0
- package/dist/delivery-health.js +19 -6
- package/dist/delivery.d.ts +2 -0
- package/dist/delivery.js +1 -0
- package/dist/harness-links.js +15 -3
- package/dist/harness.js +3 -1
- package/dist/help.js +25 -16
- package/dist/inbox.d.ts +2 -0
- package/dist/inbox.js +4 -2
- package/dist/list.js +77 -24
- package/dist/model.d.ts +1 -3
- package/dist/paseo-connection.js +9 -3
- package/dist/paseo-state.d.ts +4 -1
- package/dist/paseo-state.js +2 -2
- package/dist/presentation.d.ts +4 -0
- package/dist/presentation.js +41 -12
- package/dist/runtime.d.ts +1 -0
- package/dist/square-actions.d.ts +5 -0
- package/dist/square-actions.js +31 -7
- package/dist/square-core.d.ts +11 -1
- package/dist/square-core.js +12 -9
- package/dist/square-facade.d.ts +5 -2
- package/dist/square-file-adapter.d.ts +1 -1
- package/dist/square-file-adapter.js +11 -4
- package/dist/square-wiring.d.ts +1 -0
- package/dist/square-wiring.js +5 -1
- package/dist/stream.d.ts +8 -1
- package/dist/stream.js +17 -6
- package/dist/views.d.ts +11 -5
- package/dist/views.js +42 -16
- package/dist/wake-sink.d.ts +2 -0
- package/dist/wake-sink.js +10 -1
- package/dist/watch.js +26 -9
- package/extensions/square-pi.js +41 -7
- package/package.json +1 -1
- package/skills/brainstorm/SKILL.md +8 -6
- package/skills/square/SKILL.md +10 -10
|
@@ -3,8 +3,8 @@ import { runCodexHookAsync } from '../codex-hook.js';
|
|
|
3
3
|
import { sessionInbox } from '../inbox.js';
|
|
4
4
|
import { sweepPendingNotifications } from '../notifications.js';
|
|
5
5
|
import { cmdListSquares } from '../list.js';
|
|
6
|
-
import { parseActivityId, sameName } from '../model.js';
|
|
7
|
-
import { commandPrefix,
|
|
6
|
+
import { nameKey, parseActivityId, sameName } from '../model.js';
|
|
7
|
+
import { commandPrefix, participantIdentity, renderGrepActivitiesView, renderEventCli, renderAmbientEvent, renderPresenceAnchor, truncateChars, withPathOutput, quoteShell, } from '../presentation.js';
|
|
8
8
|
import { actId, nowMs } from '../runtime.js';
|
|
9
9
|
import { cmdStream, cmdStreamNdjson } from '../stream.js';
|
|
10
10
|
import { formatRelativeTime, formatTimestamp, parseTimeOrRelative } from '../time.js';
|
|
@@ -12,8 +12,23 @@ import { cmdWatch } from '../watch.js';
|
|
|
12
12
|
import { openSquare } from '../square-file-adapter.js';
|
|
13
13
|
import { closeOpenSquare } from '../open-square.js';
|
|
14
14
|
import { historyPresentation, participantsPresentation, statusPresentation } from '../views.js';
|
|
15
|
-
import { fail, parseDurationMs, parseNameList, parseNonNegativeInteger, readStdin, requireParticipant, requireSquarePath, requireValue, usage, } from './context.js';
|
|
15
|
+
import { fail, parseBoundedLimit, parseDurationMs, parseNameList, parseNonNegativeInteger, readStdin, requireParticipant, requireSquarePath, requireValue, usage, } from './context.js';
|
|
16
|
+
import { CATCH_DEFAULT_LIMIT, CATCH_MAX_LIMIT } from '../catch-decisions.js';
|
|
16
17
|
const STATUS_PARTICIPANT_PREVIEW_LIMIT = 10;
|
|
18
|
+
const HISTORY_DEFAULT_LIMIT = 10;
|
|
19
|
+
const HISTORY_MAX_LIMIT = 100;
|
|
20
|
+
const PARTICIPANTS_DEFAULT_LIMIT = 20;
|
|
21
|
+
const PARTICIPANTS_MAX_LIMIT = 100;
|
|
22
|
+
const INBOX_DEFAULT_LIMIT = 20;
|
|
23
|
+
const INBOX_MAX_LIMIT = 100;
|
|
24
|
+
const INBOX_DISPLAY_CHARS = 160;
|
|
25
|
+
function inboxDisplay(value) {
|
|
26
|
+
const truncated = truncateChars(value, INBOX_DISPLAY_CHARS - 1);
|
|
27
|
+
return truncated.remaining === 0 ? truncated.text : `${truncated.text}…`;
|
|
28
|
+
}
|
|
29
|
+
function inboxLimitCommand(sessionId, json) {
|
|
30
|
+
return `square inbox --for-session ${quoteShell(sessionId)} --limit ${INBOX_MAX_LIMIT}${json ? ' --json' : ''}`;
|
|
31
|
+
}
|
|
17
32
|
export const listCommand = {
|
|
18
33
|
parse: (argv) => argv,
|
|
19
34
|
async execute(argv, context) {
|
|
@@ -25,6 +40,9 @@ export const streamCommand = {
|
|
|
25
40
|
parse(argv, context) {
|
|
26
41
|
let ndjson = false;
|
|
27
42
|
let forName;
|
|
43
|
+
let last = 10;
|
|
44
|
+
let after;
|
|
45
|
+
let hasLast = false;
|
|
28
46
|
for (let index = 0; index < argv.length; index++) {
|
|
29
47
|
if (argv[index] === '--ndjson')
|
|
30
48
|
ndjson = true;
|
|
@@ -32,17 +50,32 @@ export const streamCommand = {
|
|
|
32
50
|
forName = requireValue(argv, index, argv[index]);
|
|
33
51
|
index += 1;
|
|
34
52
|
}
|
|
53
|
+
else if (argv[index] === '--last') {
|
|
54
|
+
if (after !== undefined)
|
|
55
|
+
fail('Invalid stream options: --last and --after cannot be combined.');
|
|
56
|
+
last = parseNonNegativeInteger(requireValue(argv, index, argv[index]), '--last');
|
|
57
|
+
if (last > 100)
|
|
58
|
+
fail('Invalid --last: maximum is 100.');
|
|
59
|
+
hasLast = true;
|
|
60
|
+
index += 1;
|
|
61
|
+
}
|
|
62
|
+
else if (argv[index] === '--after') {
|
|
63
|
+
if (hasLast)
|
|
64
|
+
fail('Invalid stream options: --last and --after cannot be combined.');
|
|
65
|
+
after = parseActRef(requireValue(argv, index, argv[index]), '--after');
|
|
66
|
+
index += 1;
|
|
67
|
+
}
|
|
35
68
|
else
|
|
36
69
|
usage(context.command);
|
|
37
70
|
}
|
|
38
|
-
if (
|
|
71
|
+
if (!ndjson && argv.length > 0)
|
|
39
72
|
usage(context.command);
|
|
40
|
-
return { ndjson, forName };
|
|
73
|
+
return { ndjson, forName, start: after === undefined ? { kind: 'tail', last } : { kind: 'after', after } };
|
|
41
74
|
},
|
|
42
75
|
async execute(intent, context) {
|
|
43
76
|
const squarePath = requireSquarePath(context);
|
|
44
77
|
if (intent.ndjson)
|
|
45
|
-
await cmdStreamNdjson(squarePath, intent.forName);
|
|
78
|
+
await cmdStreamNdjson(squarePath, intent.forName, intent.start);
|
|
46
79
|
else
|
|
47
80
|
await cmdStream(squarePath);
|
|
48
81
|
},
|
|
@@ -53,6 +86,7 @@ export const catchCommand = {
|
|
|
53
86
|
const name = requireParticipant(context.name);
|
|
54
87
|
let idleMs;
|
|
55
88
|
let mention;
|
|
89
|
+
let limit = CATCH_DEFAULT_LIMIT;
|
|
56
90
|
let replace = false;
|
|
57
91
|
let now = false;
|
|
58
92
|
const participants = [];
|
|
@@ -66,14 +100,11 @@ export const catchCommand = {
|
|
|
66
100
|
index += 1;
|
|
67
101
|
}
|
|
68
102
|
else if (argv[index] === '--mention') {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
else {
|
|
75
|
-
mention = name;
|
|
76
|
-
}
|
|
103
|
+
mention = name;
|
|
104
|
+
}
|
|
105
|
+
else if (argv[index] === '--limit') {
|
|
106
|
+
limit = parseBoundedLimit(argv[index + 1], '--limit', CATCH_MAX_LIMIT, `${commandPrefix(requireSquarePath(context))} catch --now --limit ${CATCH_MAX_LIMIT}`);
|
|
107
|
+
index += 1;
|
|
77
108
|
}
|
|
78
109
|
else if (argv[index] === '--replace')
|
|
79
110
|
replace = true;
|
|
@@ -89,6 +120,7 @@ export const catchCommand = {
|
|
|
89
120
|
return {
|
|
90
121
|
...(participants.length > 0 ? { participants } : {}),
|
|
91
122
|
...(mention === undefined ? {} : { mention }),
|
|
123
|
+
limit,
|
|
92
124
|
...(idleMs === undefined ? {} : { idleMs }),
|
|
93
125
|
...(replace ? { replace } : {}),
|
|
94
126
|
...(now ? { now } : {}),
|
|
@@ -135,8 +167,7 @@ function historyContinuationArgs(argv) {
|
|
|
135
167
|
}
|
|
136
168
|
function parseHistory(argv, context) {
|
|
137
169
|
const squarePath = requireSquarePath(context);
|
|
138
|
-
|
|
139
|
-
let lastN = 10;
|
|
170
|
+
let lastN = HISTORY_DEFAULT_LIMIT;
|
|
140
171
|
let lastNExplicit = false;
|
|
141
172
|
let after;
|
|
142
173
|
let afterIndex;
|
|
@@ -145,7 +176,6 @@ function parseHistory(argv, context) {
|
|
|
145
176
|
let beforeContext;
|
|
146
177
|
let afterContext;
|
|
147
178
|
let mention;
|
|
148
|
-
let pending = false;
|
|
149
179
|
let noTruncate = false;
|
|
150
180
|
let grep;
|
|
151
181
|
let fixed;
|
|
@@ -156,14 +186,7 @@ function parseHistory(argv, context) {
|
|
|
156
186
|
for (let index = 0; index < argv.length; index++) {
|
|
157
187
|
const flag = argv[index];
|
|
158
188
|
if (flag === '--limit') {
|
|
159
|
-
|
|
160
|
-
const retry = `${commandPrefix(squarePath)} history --limit 30`;
|
|
161
|
-
if (value === undefined || value.startsWith('--'))
|
|
162
|
-
fail(`✕ --limit needs a positive number\n» ${retry}`);
|
|
163
|
-
if (!/^[1-9]\d*$/.test(value) || !Number.isSafeInteger(Number(value))) {
|
|
164
|
-
fail(`✕ --limit needs a positive number\n» ${retry}`);
|
|
165
|
-
}
|
|
166
|
-
lastN = Number(value);
|
|
189
|
+
lastN = parseBoundedLimit(argv[index + 1], '--limit', HISTORY_MAX_LIMIT, `${commandPrefix(squarePath)} history --limit ${HISTORY_MAX_LIMIT}`);
|
|
167
190
|
lastNExplicit = true;
|
|
168
191
|
index += 1;
|
|
169
192
|
}
|
|
@@ -210,8 +233,6 @@ function parseHistory(argv, context) {
|
|
|
210
233
|
mention = requireValue(argv, index, flag);
|
|
211
234
|
index += 1;
|
|
212
235
|
}
|
|
213
|
-
else if (flag === '--pending')
|
|
214
|
-
pending = true;
|
|
215
236
|
else if (flag === '--grep') {
|
|
216
237
|
grep = requireValue(argv, index, flag);
|
|
217
238
|
index += 1;
|
|
@@ -236,15 +257,13 @@ function parseHistory(argv, context) {
|
|
|
236
257
|
else
|
|
237
258
|
fail(`✕ history does not know ${flag}\n» square history --help`);
|
|
238
259
|
}
|
|
239
|
-
if (pending && !viewer)
|
|
240
|
-
fail('--pending requires --as <name>.');
|
|
241
260
|
if (grep !== undefined && fixed !== undefined)
|
|
242
261
|
fail('--grep and --fixed cannot be combined.');
|
|
243
262
|
if (grep === '' || fixed === '')
|
|
244
263
|
fail('--grep and --fixed require non-empty text.');
|
|
245
264
|
if (beforeIndex !== undefined && afterIndex !== undefined)
|
|
246
265
|
fail('--before and --after cannot be combined.');
|
|
247
|
-
if (!lastNExplicit &&
|
|
266
|
+
if (!lastNExplicit && atIndexes.length > 0)
|
|
248
267
|
lastN = null;
|
|
249
268
|
return {
|
|
250
269
|
lastN,
|
|
@@ -256,8 +275,6 @@ function parseHistory(argv, context) {
|
|
|
256
275
|
beforeContext,
|
|
257
276
|
afterContext,
|
|
258
277
|
mention,
|
|
259
|
-
pending,
|
|
260
|
-
viewer,
|
|
261
278
|
noTruncate,
|
|
262
279
|
grep,
|
|
263
280
|
fixed,
|
|
@@ -268,11 +285,14 @@ function parseHistory(argv, context) {
|
|
|
268
285
|
};
|
|
269
286
|
}
|
|
270
287
|
function historyContinuationCommand(options, squarePath, direction, index) {
|
|
271
|
-
const
|
|
272
|
-
|
|
273
|
-
|
|
288
|
+
const args = [...(options.continuationArgs ?? []), direction, actId(index), '--limit', String(options.lastN ?? HISTORY_DEFAULT_LIMIT)];
|
|
289
|
+
return `${commandPrefix(squarePath)} history ${args.map((arg) => arg.startsWith('-') || /^act\/\d+$/.test(arg) || /^\d+$/.test(arg) ? arg : quoteShell(arg)).join(' ')}`;
|
|
290
|
+
}
|
|
291
|
+
function boundedHistoryCommand(options, squarePath) {
|
|
292
|
+
const args = [...(options.continuationArgs ?? []), '--limit', String(HISTORY_MAX_LIMIT)];
|
|
293
|
+
return `${commandPrefix(squarePath)} history ${args.map((arg) => arg.startsWith('-') || /^act\/\d+$/.test(arg) || /^\d+$/.test(arg) ? arg : quoteShell(arg)).join(' ')}`;
|
|
274
294
|
}
|
|
275
|
-
function renderFields(sayNumbers, item, fields
|
|
295
|
+
function renderFields(sayNumbers, item, fields) {
|
|
276
296
|
return fields.map((field) => {
|
|
277
297
|
switch (field) {
|
|
278
298
|
case 'id': return actId(item.index);
|
|
@@ -281,14 +301,14 @@ function renderFields(sayNumbers, item, fields, perception) {
|
|
|
281
301
|
case 'ts':
|
|
282
302
|
case 'at': return formatTimestamp(item.at);
|
|
283
303
|
case 'kind': return item.kind;
|
|
284
|
-
case 'body': return
|
|
304
|
+
case 'body': return 'body' in item && typeof item.body === 'string' ? item.body.replace(/\s+/g, ' ').trim() : '';
|
|
285
305
|
case 'number': return item.kind === 'say' ? String(sayNumbers[item.index]) : '';
|
|
286
306
|
case 'reply': return item.kind === 'say' && item.reply !== undefined ? actId(item.reply) : '';
|
|
287
307
|
default: return '';
|
|
288
308
|
}
|
|
289
309
|
}).join('\t');
|
|
290
310
|
}
|
|
291
|
-
function jsonLine(sayNumbers, item
|
|
311
|
+
function jsonLine(sayNumbers, item) {
|
|
292
312
|
const act = item;
|
|
293
313
|
return JSON.stringify({
|
|
294
314
|
id: actId(item.index),
|
|
@@ -297,25 +317,23 @@ function jsonLine(sayNumbers, item, perception) {
|
|
|
297
317
|
author: act.actor ?? null,
|
|
298
318
|
at: act.at,
|
|
299
319
|
ts: formatTimestamp(act.at),
|
|
300
|
-
body:
|
|
320
|
+
body: 'body' in act && typeof act.body === 'string' ? act.body : '',
|
|
301
321
|
number: act.kind === 'say' ? sayNumbers[act.index] : null,
|
|
322
|
+
mentions: act.kind === 'say' ? [...(act.mentions ?? [])] : [],
|
|
302
323
|
reach: act.kind === 'say' ? act.reach ?? null : null,
|
|
303
324
|
reply: act.kind === 'say' && act.reply !== undefined ? actId(act.reply) : null,
|
|
304
325
|
});
|
|
305
326
|
}
|
|
306
|
-
function renderHistoryProjection(projection, visible, noTruncate, squarePath
|
|
327
|
+
function renderHistoryProjection(projection, visible, noTruncate, squarePath) {
|
|
307
328
|
const shown = visible.filter((activity) => activity.kind === 'say' || activity.kind === 'done');
|
|
308
|
-
const preview = noTruncate ? undefined : 200;
|
|
329
|
+
const preview = noTruncate || shown.length <= 1 ? undefined : 200;
|
|
309
330
|
const chunks = [];
|
|
310
331
|
for (const activity of shown) {
|
|
311
332
|
const options = {
|
|
312
333
|
preview,
|
|
313
334
|
actNumber: activity.kind === 'say' ? projection.sayNumbers[activity.index] : undefined,
|
|
314
|
-
perception: activity.perception,
|
|
315
335
|
};
|
|
316
|
-
const rendered =
|
|
317
|
-
? renderEventCli(activity, options)
|
|
318
|
-
: renderAmbientEvent(activity, viewer, options);
|
|
336
|
+
const rendered = renderEventCli(activity, options);
|
|
319
337
|
if (rendered !== '')
|
|
320
338
|
chunks.push(rendered);
|
|
321
339
|
const participants = projection.presenceAnchors[activity.index];
|
|
@@ -324,7 +342,7 @@ function renderHistoryProjection(projection, visible, noTruncate, squarePath, vi
|
|
|
324
342
|
}
|
|
325
343
|
if (chunks.length === 0)
|
|
326
344
|
return 'latest\n ○ no public activity in this view';
|
|
327
|
-
if (preview !== undefined && shown.some((activity) => activity.kind === 'say' && activity.body.length > preview
|
|
345
|
+
if (preview !== undefined && shown.some((activity) => activity.kind === 'say' && activity.body.length > preview)) {
|
|
328
346
|
chunks.push(`» ${commandPrefix(squarePath)} history --no-truncate`);
|
|
329
347
|
}
|
|
330
348
|
return chunks.join('\n\n');
|
|
@@ -339,6 +357,9 @@ export const historyCommand = {
|
|
|
339
357
|
// then --order only changes how the selected page is displayed.
|
|
340
358
|
const projection = await historyPresentation(square, { ...options, order: 'asc' });
|
|
341
359
|
let events = [...projection.activities];
|
|
360
|
+
if (options.lastN === null && events.length > HISTORY_MAX_LIMIT) {
|
|
361
|
+
fail(`✕ history is capped at ${HISTORY_MAX_LIMIT} activities\n» ${boundedHistoryCommand(options, squarePath)}`);
|
|
362
|
+
}
|
|
342
363
|
const searching = options.grep !== undefined || options.fixed !== undefined;
|
|
343
364
|
const totalMatches = searching ? events.length : 0;
|
|
344
365
|
if (options.lastN != null) {
|
|
@@ -349,16 +370,14 @@ export const historyCommand = {
|
|
|
349
370
|
if (options.order === 'desc')
|
|
350
371
|
events.reverse();
|
|
351
372
|
if (options.json)
|
|
352
|
-
return events.map((item) => jsonLine(projection.sayNumbers, item
|
|
373
|
+
return events.map((item) => jsonLine(projection.sayNumbers, item)).join('\n') + (events.length > 0 ? '\n' : '');
|
|
353
374
|
if (options.format !== undefined && options.format.length > 0) {
|
|
354
|
-
return events.map((item) => renderFields(projection.sayNumbers, item, options.format
|
|
375
|
+
return events.map((item) => renderFields(projection.sayNumbers, item, options.format)).join('\n') + (events.length > 0 ? '\n' : '');
|
|
355
376
|
}
|
|
356
377
|
const pattern = options.grep ?? options.fixed;
|
|
357
|
-
const anonymous = options.viewer === undefined;
|
|
358
|
-
const archive = anonymous;
|
|
359
378
|
const output = pattern === undefined || pattern === ''
|
|
360
|
-
? renderHistoryProjection(projection, events, options.noTruncate === true, squarePath
|
|
361
|
-
: renderGrepActivitiesView(events, totalMatches, options.noTruncate, squarePath, pattern, options.fixed !== undefined, (
|
|
379
|
+
? renderHistoryProjection(projection, events, options.noTruncate === true, squarePath)
|
|
380
|
+
: renderGrepActivitiesView(events, totalMatches, options.noTruncate, squarePath, pattern, options.fixed !== undefined, () => 'full');
|
|
362
381
|
const publicEvents = events.filter((item) => item.kind === 'say' || item.kind === 'done');
|
|
363
382
|
const allPublic = projection.activities.filter((item) => item.kind === 'say' || item.kind === 'done');
|
|
364
383
|
const pageMin = publicEvents.length === 0 ? undefined : Math.min(...publicEvents.map((item) => item.index));
|
|
@@ -377,23 +396,43 @@ export const historyCommand = {
|
|
|
377
396
|
},
|
|
378
397
|
present: (result) => process.stdout.write(result),
|
|
379
398
|
};
|
|
399
|
+
function participantsLimitCommand(squarePath, limit) {
|
|
400
|
+
return `${commandPrefix(squarePath)} participants --limit ${limit}`;
|
|
401
|
+
}
|
|
402
|
+
function parseParticipants(argv, context) {
|
|
403
|
+
let limit = PARTICIPANTS_DEFAULT_LIMIT;
|
|
404
|
+
for (let index = 0; index < argv.length; index++) {
|
|
405
|
+
if (argv[index] !== '--limit')
|
|
406
|
+
usage(context.command);
|
|
407
|
+
limit = parseBoundedLimit(argv[index + 1], '--limit', PARTICIPANTS_MAX_LIMIT, participantsLimitCommand(requireSquarePath(context), PARTICIPANTS_MAX_LIMIT));
|
|
408
|
+
index += 1;
|
|
409
|
+
}
|
|
410
|
+
return { limit };
|
|
411
|
+
}
|
|
380
412
|
export const participantsCommand = {
|
|
381
|
-
parse(argv, context) {
|
|
382
|
-
|
|
383
|
-
async execute(_intent, context) {
|
|
413
|
+
parse(argv, context) { return parseParticipants(argv, context); },
|
|
414
|
+
async execute(intent, context) {
|
|
384
415
|
const squarePath = requireSquarePath(context);
|
|
385
416
|
const square = await openSquare(squarePath, { clock: nowMs });
|
|
386
417
|
try {
|
|
387
418
|
const now = nowMs();
|
|
388
419
|
const participants = await participantsPresentation(square);
|
|
389
|
-
const lines = participants.map((participant) => {
|
|
420
|
+
const lines = participants.slice(0, intent.limit).map((participant) => {
|
|
390
421
|
const glyph = participant.state === 'done' ? '○' : participant.presence === 'watching' ? '◎' : participant.activityCount > 0 ? '●' : '○';
|
|
391
422
|
const state = participant.state === 'done' ? 'done' : participant.presence === 'watching' ? 'catching' : participant.state;
|
|
392
423
|
const last = participant.lastActiveAt === undefined ? '—' : formatRelativeTime(participant.lastActiveAt, now);
|
|
393
|
-
return ` ${glyph} ${
|
|
424
|
+
return ` ${glyph} ${participant.name} · ${state} · ${participant.activityCount} ${participant.activityCount === 1 ? 'activity' : 'activities'} · ${last}`;
|
|
394
425
|
});
|
|
395
426
|
const participantCount = participants.filter((participant) => participant.state === 'active').length;
|
|
396
|
-
|
|
427
|
+
const tail = participants.length <= intent.limit
|
|
428
|
+
? []
|
|
429
|
+
: [
|
|
430
|
+
` ○ ${lines.length} of ${participants.length} participants shown`,
|
|
431
|
+
...(participants.length <= PARTICIPANTS_MAX_LIMIT ? [
|
|
432
|
+
`» ${participantsLimitCommand(squarePath, participants.length)}`,
|
|
433
|
+
] : []),
|
|
434
|
+
];
|
|
435
|
+
return withPathOutput(squarePath, ['participants', ...lines, ...tail].join('\n'), {
|
|
397
436
|
participantCount,
|
|
398
437
|
});
|
|
399
438
|
}
|
|
@@ -440,7 +479,9 @@ export const statusCommand = {
|
|
|
440
479
|
});
|
|
441
480
|
if (active.length > STATUS_PARTICIPANT_PREVIEW_LIMIT) {
|
|
442
481
|
people.push(` ○ … ${active.length - STATUS_PARTICIPANT_PREVIEW_LIMIT} more participants`);
|
|
443
|
-
people.push(`» ${
|
|
482
|
+
people.push(`» ${result.participants.length <= PARTICIPANTS_MAX_LIMIT
|
|
483
|
+
? participantsLimitCommand(squarePath, result.participants.length)
|
|
484
|
+
: `${commandPrefix(squarePath)} participants`}`);
|
|
444
485
|
}
|
|
445
486
|
const cap = result.hardCap === null ? 'unlimited' : String(result.hardCap);
|
|
446
487
|
const hold = result.holdActive
|
|
@@ -462,8 +503,7 @@ export const statusCommand = {
|
|
|
462
503
|
: ' · latest activity is private to another participant']
|
|
463
504
|
: [` ${visible.replace(/\n/g, '\n ')}`];
|
|
464
505
|
if (visible.includes('more chars') && result.latestAct !== undefined) {
|
|
465
|
-
|
|
466
|
-
latest.push(`» ${prefix} history --at ${actId(result.latestAct)} -C 2 --no-truncate`);
|
|
506
|
+
latest.push(`» ${commandPrefix(squarePath)} history --at ${actId(result.latestAct)} -C 2 --no-truncate`);
|
|
467
507
|
}
|
|
468
508
|
const output = [
|
|
469
509
|
`${result.activeCount} active · ${result.doneCount} done · cap ${cap} · throttle ${result.throttlePerMinute === undefined ? 'none' : `${result.throttlePerMinute}/min`}`,
|
|
@@ -481,6 +521,9 @@ export const inboxCommand = {
|
|
|
481
521
|
parse(argv, context) {
|
|
482
522
|
let sessionId;
|
|
483
523
|
let json = false;
|
|
524
|
+
let limitValue;
|
|
525
|
+
let hasLimit = false;
|
|
526
|
+
let duplicateLimit = false;
|
|
484
527
|
for (let index = 0; index < argv.length; index++) {
|
|
485
528
|
if (argv[index] === '--for-session') {
|
|
486
529
|
sessionId = requireValue(argv, index, argv[index]);
|
|
@@ -488,16 +531,59 @@ export const inboxCommand = {
|
|
|
488
531
|
}
|
|
489
532
|
else if (argv[index] === '--json')
|
|
490
533
|
json = true;
|
|
534
|
+
else if (argv[index] === '--limit') {
|
|
535
|
+
if (hasLimit)
|
|
536
|
+
duplicateLimit = true;
|
|
537
|
+
hasLimit = true;
|
|
538
|
+
const value = argv[index + 1];
|
|
539
|
+
if (value !== undefined && !value.startsWith('--')) {
|
|
540
|
+
limitValue = value;
|
|
541
|
+
index += 1;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
491
544
|
else
|
|
492
545
|
usage(context.command);
|
|
493
546
|
}
|
|
494
547
|
if (!sessionId)
|
|
495
548
|
fail('inbox requires --for-session <session-id>.');
|
|
496
|
-
|
|
549
|
+
const retry = inboxLimitCommand(sessionId, json);
|
|
550
|
+
if (duplicateLimit)
|
|
551
|
+
fail(`✕ inbox accepts one --limit\n» ${retry}`);
|
|
552
|
+
const limit = hasLimit
|
|
553
|
+
? parseBoundedLimit(limitValue, '--limit', INBOX_MAX_LIMIT, retry)
|
|
554
|
+
: INBOX_DEFAULT_LIMIT;
|
|
555
|
+
return { sessionId, json, limit };
|
|
497
556
|
},
|
|
498
557
|
async execute(intent) {
|
|
499
558
|
const inbox = await sessionInbox(intent.sessionId);
|
|
500
|
-
|
|
559
|
+
const ordered = [...inbox].sort((left, right) => {
|
|
560
|
+
if (left.squarePath < right.squarePath)
|
|
561
|
+
return -1;
|
|
562
|
+
if (left.squarePath > right.squarePath)
|
|
563
|
+
return 1;
|
|
564
|
+
const leftName = nameKey(left.name);
|
|
565
|
+
const rightName = nameKey(right.name);
|
|
566
|
+
if (leftName < rightName)
|
|
567
|
+
return -1;
|
|
568
|
+
if (leftName > rightName)
|
|
569
|
+
return 1;
|
|
570
|
+
if (left.name < right.name)
|
|
571
|
+
return -1;
|
|
572
|
+
if (left.name > right.name)
|
|
573
|
+
return 1;
|
|
574
|
+
return 0;
|
|
575
|
+
});
|
|
576
|
+
const rows = ordered.slice(0, intent.limit).map((membership) => ({
|
|
577
|
+
namePreview: inboxDisplay(membership.name),
|
|
578
|
+
squarePathPreview: inboxDisplay(membership.squarePath),
|
|
579
|
+
pending: membership.notifications.length,
|
|
580
|
+
}));
|
|
581
|
+
if (intent.json)
|
|
582
|
+
return `${JSON.stringify({ rows, total: ordered.length })}\n`;
|
|
583
|
+
return [
|
|
584
|
+
...rows.map((row) => `${row.namePreview}\t${row.squarePathPreview}\t${row.pending}\n`),
|
|
585
|
+
...(rows.length < ordered.length ? [`${rows.length} of ${ordered.length} memberships shown\n`] : []),
|
|
586
|
+
].join('');
|
|
501
587
|
},
|
|
502
588
|
present: (result) => process.stdout.write(result),
|
|
503
589
|
};
|
package/dist/cli/program.js
CHANGED
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
import { helpRequest } from '../help.js';
|
|
2
2
|
import { isSquareError } from '../model.js';
|
|
3
|
+
import { commandPrefix } from '../presentation.js';
|
|
3
4
|
import { defaultContext, parseGlobalArgs } from './context.js';
|
|
4
5
|
import { executeRegisteredCommand, findCommand } from './registry.js';
|
|
5
|
-
function handleSquareError(error) {
|
|
6
|
+
function handleSquareError(error, squarePath) {
|
|
6
7
|
if (isSquareError(error)) {
|
|
7
8
|
process.stderr.write(`${error.message}\n`);
|
|
9
|
+
if (squarePath !== undefined && /^(Unknown participant|Unknown mention target)/.test(error.message)) {
|
|
10
|
+
process.stderr.write(`» ${commandPrefix(squarePath)} participants\n`);
|
|
11
|
+
}
|
|
8
12
|
process.exit(error.code === 'not_found' ? 1 : 2);
|
|
9
13
|
}
|
|
10
14
|
throw error;
|
|
11
15
|
}
|
|
12
16
|
/** Parse global flags, select an executable adapter, and leave all command work to the registry. */
|
|
13
17
|
export async function runCli(rawArgs = process.argv.slice(2)) {
|
|
18
|
+
let squarePath;
|
|
14
19
|
try {
|
|
15
20
|
const requestedHelp = helpRequest(rawArgs);
|
|
16
21
|
if (requestedHelp !== undefined) {
|
|
@@ -18,6 +23,7 @@ export async function runCli(rawArgs = process.argv.slice(2)) {
|
|
|
18
23
|
return;
|
|
19
24
|
}
|
|
20
25
|
const parsed = await parseGlobalArgs(rawArgs);
|
|
26
|
+
squarePath = parsed.squarePath;
|
|
21
27
|
if (parsed.args.length === 0 || parsed.args[0] === '--help' || parsed.args[0] === '-h') {
|
|
22
28
|
await executeRegisteredCommand('help', [], defaultContext('help', parsed.squarePath, parsed.name));
|
|
23
29
|
return;
|
|
@@ -30,6 +36,6 @@ export async function runCli(rawArgs = process.argv.slice(2)) {
|
|
|
30
36
|
await executeRegisteredCommand(command, parsed.args.slice(1), defaultContext(command, parsed.squarePath, parsed.name));
|
|
31
37
|
}
|
|
32
38
|
catch (error) {
|
|
33
|
-
handleSquareError(error);
|
|
39
|
+
handleSquareError(error, squarePath);
|
|
34
40
|
}
|
|
35
41
|
}
|
|
@@ -8,7 +8,7 @@ interface BuildIntent {
|
|
|
8
8
|
}
|
|
9
9
|
interface JoinIntent {
|
|
10
10
|
name: string;
|
|
11
|
-
lastN: number
|
|
11
|
+
lastN: number;
|
|
12
12
|
kick: boolean;
|
|
13
13
|
}
|
|
14
14
|
interface ActivityIntent {
|
|
@@ -16,6 +16,8 @@ interface ActivityIntent {
|
|
|
16
16
|
activity: string;
|
|
17
17
|
force: boolean;
|
|
18
18
|
noWait: boolean;
|
|
19
|
+
noMention: boolean;
|
|
20
|
+
mentions: string[];
|
|
19
21
|
reach?: Reach;
|
|
20
22
|
reply?: number;
|
|
21
23
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { cmdActivity } from '../activity.js';
|
|
2
2
|
import { formatActivityId, formatHardCap, parseActivityId, } from '../model.js';
|
|
3
|
-
import { participantCommandPrefix, participantIdentity, renderEventCli, renderAmbientEvent, withPathOutput, } from '../presentation.js';
|
|
4
|
-
import { hasAutomaticDeliveryIdentity, } from '../registry.js';
|
|
3
|
+
import { participantCommandPrefix, participantIdentity, quoteShell, renderEventCli, renderAmbientEvent, withPathOutput, } from '../presentation.js';
|
|
4
|
+
import { hasAutomaticDeliveryIdentity, lookupParticipant, recordSessionDone, } from '../registry.js';
|
|
5
5
|
import { createHostLedgerPort } from '../host-ledger-file-adapter.js';
|
|
6
6
|
import { projectLocalParticipantBinding, sessionIdsFromEnvironment } from '../square-projections.js';
|
|
7
7
|
import { nowMs } from '../runtime.js';
|
|
@@ -10,7 +10,9 @@ import { closeOpenSquare } from '../open-square.js';
|
|
|
10
10
|
import { openParticipant, Square } from '../square-wiring.js';
|
|
11
11
|
import { entryPresentation, eventPresentation } from '../views.js';
|
|
12
12
|
import { createDefaultWakeTransport } from '../notifications.js';
|
|
13
|
-
import { fail, parseHardCap, parsePositiveInteger, readStdin, requireParticipant, requireSquarePath, requireValue, resolveBody, usage, } from './context.js';
|
|
13
|
+
import { fail, parseBoundedLimit, parseHardCap, parsePositiveInteger, readStdin, requireParticipant, requireSquarePath, requireValue, resolveBody, usage, } from './context.js';
|
|
14
|
+
const JOIN_DEFAULT_LAST = 10;
|
|
15
|
+
const JOIN_MAX_LAST = 100;
|
|
14
16
|
function parseBuild(argv) {
|
|
15
17
|
const options = { force: false, hardCap: null };
|
|
16
18
|
for (let index = 0; index < argv.length; index++) {
|
|
@@ -60,16 +62,15 @@ export const buildCommand = {
|
|
|
60
62
|
present: (result) => process.stdout.write(result),
|
|
61
63
|
};
|
|
62
64
|
function parseJoin(argv, context) {
|
|
63
|
-
|
|
65
|
+
const name = requireParticipant(context.name);
|
|
66
|
+
const squarePath = requireSquarePath(context);
|
|
67
|
+
let lastN = JOIN_DEFAULT_LAST;
|
|
64
68
|
let kick = false;
|
|
65
69
|
for (let index = 0; index < argv.length; index++) {
|
|
66
70
|
if (argv[index] === '--last') {
|
|
67
|
-
lastN =
|
|
71
|
+
lastN = parseBoundedLimit(argv[index + 1], '--last', JOIN_MAX_LAST, `${participantCommandPrefix(squarePath, name)} join --last ${JOIN_MAX_LAST}`);
|
|
68
72
|
index += 1;
|
|
69
73
|
}
|
|
70
|
-
else if (argv[index] === '--all') {
|
|
71
|
-
lastN = null;
|
|
72
|
-
}
|
|
73
74
|
else if (argv[index] === '--kick') {
|
|
74
75
|
kick = true;
|
|
75
76
|
}
|
|
@@ -77,7 +78,7 @@ function parseJoin(argv, context) {
|
|
|
77
78
|
usage(context.command);
|
|
78
79
|
}
|
|
79
80
|
}
|
|
80
|
-
return { name
|
|
81
|
+
return { name, lastN, kick };
|
|
81
82
|
}
|
|
82
83
|
export const joinCommand = {
|
|
83
84
|
parse: parseJoin,
|
|
@@ -96,17 +97,27 @@ export const joinCommand = {
|
|
|
96
97
|
participant: intent.name,
|
|
97
98
|
sessionIds: sessionIdsFromEnvironment(),
|
|
98
99
|
}) !== undefined;
|
|
99
|
-
const participant = await square.join(intent.name);
|
|
100
|
-
const joinedName = participant.name;
|
|
101
100
|
const isRejoin = before.joined;
|
|
102
101
|
if (isRejoin && !intent.kick && !reconnect) {
|
|
103
102
|
fail([
|
|
104
|
-
`✕ ${participantIdentity(
|
|
103
|
+
`✕ ${participantIdentity(intent.name)} shoos you out of the square`,
|
|
105
104
|
` · a same-named participant stands here — the name is taken`,
|
|
106
105
|
` · --kick banishes her and the name becomes yours`,
|
|
107
|
-
`» ${participantCommandPrefix(squarePath,
|
|
106
|
+
`» ${participantCommandPrefix(squarePath, intent.name)} join --kick`,
|
|
108
107
|
].join('\n'));
|
|
109
108
|
}
|
|
109
|
+
const takeoverNeeded = isRejoin && intent.kick && !reconnect;
|
|
110
|
+
const oldBindings = takeoverNeeded ? await lookupParticipant(squarePath, intent.name) : [];
|
|
111
|
+
const participant = takeoverNeeded ? await square.takeover(intent.name, oldBindings.map((binding) => binding.sessionId)) : await square.join(intent.name);
|
|
112
|
+
const joinedName = participant.name;
|
|
113
|
+
if (takeoverNeeded) {
|
|
114
|
+
const currentSessions = new Set(sessionIdsFromEnvironment());
|
|
115
|
+
for (const binding of oldBindings) {
|
|
116
|
+
if (!currentSessions.has(binding.sessionId)) {
|
|
117
|
+
await recordSessionDone(binding.sessionId, binding.name, binding.squarePath, binding.channel).catch(() => undefined);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
110
121
|
const afterSquare = await openSquare(squarePath, { clock: nowMs });
|
|
111
122
|
const after = await entryPresentation(afterSquare, joinedName, intent.lastN);
|
|
112
123
|
await closeOpenSquare(afterSquare);
|
|
@@ -146,6 +157,8 @@ function parseActivity(argv, context) {
|
|
|
146
157
|
let force = false;
|
|
147
158
|
let noWait = false;
|
|
148
159
|
let bell = false;
|
|
160
|
+
let noMention = false;
|
|
161
|
+
const mentions = [];
|
|
149
162
|
let reply;
|
|
150
163
|
const bodyArgs = [];
|
|
151
164
|
for (let index = 0; index < argv.length; index++) {
|
|
@@ -158,6 +171,12 @@ function parseActivity(argv, context) {
|
|
|
158
171
|
fail('✕ express does not know --beside\n» square express --help');
|
|
159
172
|
else if (argument === '--bell')
|
|
160
173
|
bell = true;
|
|
174
|
+
else if (argument === '--no-mention')
|
|
175
|
+
noMention = true;
|
|
176
|
+
else if (argument === '--mention') {
|
|
177
|
+
mentions.push(requireParticipant(requireValue(argv, index, argument)));
|
|
178
|
+
index += 1;
|
|
179
|
+
}
|
|
161
180
|
else if (argument === '--reply') {
|
|
162
181
|
const replyIndex = parseActivityId(requireValue(argv, index, argument));
|
|
163
182
|
if (replyIndex === undefined)
|
|
@@ -169,24 +188,31 @@ function parseActivity(argv, context) {
|
|
|
169
188
|
bodyArgs.push(argument);
|
|
170
189
|
}
|
|
171
190
|
const reach = bell ? 'bell' : undefined;
|
|
191
|
+
if (bell && (noMention || mentions.length > 0))
|
|
192
|
+
fail('✕ --bell cannot be combined with --mention or --no-mention\n» square express --help');
|
|
193
|
+
if (noMention && mentions.length > 0)
|
|
194
|
+
fail('✕ --no-mention cannot be combined with --mention\n» square express --help');
|
|
195
|
+
if (!bell && !noMention && mentions.length === 0)
|
|
196
|
+
fail('✕ express needs --mention <name>, --no-mention, or --bell\n» square express --help');
|
|
172
197
|
if (bodyArgs.length !== 1) {
|
|
173
198
|
if (bodyArgs.length === 0) {
|
|
174
199
|
if (!process.stdin.isTTY)
|
|
175
|
-
return { name: requireParticipant(context.name), activity: '-', force, noWait, reach, reply };
|
|
200
|
+
return { name: requireParticipant(context.name), activity: '-', force, noWait, noMention, mentions, reach, reply };
|
|
176
201
|
}
|
|
177
202
|
fail("express requires a body argument (a quoted string or '-' with piped stdin)");
|
|
178
203
|
}
|
|
179
|
-
return { name: requireParticipant(context.name), activity: bodyArgs[0], force, noWait, reach, reply };
|
|
204
|
+
return { name: requireParticipant(context.name), activity: bodyArgs[0], force, noWait, noMention, mentions, reach, reply };
|
|
180
205
|
}
|
|
181
206
|
export const expressCommand = {
|
|
182
207
|
parse: parseActivity,
|
|
183
208
|
async execute(intent, context) {
|
|
184
209
|
const squarePath = requireSquarePath(context);
|
|
185
210
|
const body = await resolveBody(intent.activity);
|
|
186
|
-
const reachArg = intent.reach === 'bell' ? ' --bell' : '';
|
|
211
|
+
const reachArg = intent.reach === 'bell' ? ' --bell' : intent.noMention ? ' --no-mention' : intent.mentions.map((name) => ` --mention ${quoteShell(name)}`).join('');
|
|
187
212
|
await cmdActivity(squarePath, intent.name, body, (value) => value, {
|
|
188
213
|
force: intent.force,
|
|
189
214
|
noWait: intent.noWait,
|
|
215
|
+
mentions: intent.mentions,
|
|
190
216
|
reach: intent.reach,
|
|
191
217
|
reply: intent.reply,
|
|
192
218
|
forceCommand: `${participantCommandPrefix(squarePath, intent.name)} express --force${reachArg}${intent.reply === undefined ? '' : ` --reply ${formatActivityId(intent.reply)}`} -`,
|
package/dist/codex-queue.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export declare class CodexQueueSendError extends Error {
|
|
|
10
10
|
constructor(message: string, kind: CodexQueueFailureKind);
|
|
11
11
|
}
|
|
12
12
|
export declare function sendCodexQueue({ threadId, message }: CodexQueueRequest, opts?: {
|
|
13
|
+
args?: string[];
|
|
13
14
|
bin?: string;
|
|
14
15
|
env?: NodeJS.ProcessEnv;
|
|
15
16
|
timeoutMs?: number;
|
package/dist/codex-queue.js
CHANGED
|
@@ -16,7 +16,7 @@ function classifyFailure(message) {
|
|
|
16
16
|
return 'unknown';
|
|
17
17
|
}
|
|
18
18
|
export function sendCodexQueue({ threadId, message }, opts = {}) {
|
|
19
|
-
const result = spawnSync(opts.bin ?? process.env.SQUARE_CODEX_BIN ?? 'codex', ['queue', '--thread', threadId, '--message', message], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], timeout: opts.timeoutMs ?? 5000, env: opts.env ?? process.env });
|
|
19
|
+
const result = spawnSync(opts.bin ?? process.env.SQUARE_CODEX_BIN ?? 'codex', [...(opts.args ?? []), 'queue', '--thread', threadId, '--message', message], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], timeout: opts.timeoutMs ?? 5000, env: opts.env ?? process.env });
|
|
20
20
|
if (result.error) {
|
|
21
21
|
const messageText = result.error.message;
|
|
22
22
|
throw new CodexQueueSendError(messageText, classifyFailure(messageText));
|