@detergent-software/conductor 1.0.0-dev.1 → 1.0.0-dev.10
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/CHANGELOG.md +87 -0
- package/README.md +175 -9
- package/build/cli.js +368 -21
- package/build/cli.js.map +1 -1
- package/build/clock.js +17 -0
- package/build/clock.js.map +1 -0
- package/build/conditions.js +5 -6
- package/build/conditions.js.map +1 -1
- package/build/config.js +92 -1
- package/build/config.js.map +1 -1
- package/build/credential.js +52 -0
- package/build/credential.js.map +1 -0
- package/build/daemon.js +56 -1
- package/build/daemon.js.map +1 -1
- package/build/events.js +106 -0
- package/build/events.js.map +1 -0
- package/build/inbox.js +385 -0
- package/build/inbox.js.map +1 -0
- package/build/manager.js +26 -3
- package/build/manager.js.map +1 -1
- package/build/mod.js +51 -0
- package/build/mod.js.map +1 -0
- package/build/note.js +71 -0
- package/build/note.js.map +1 -0
- package/build/notes.js +188 -0
- package/build/notes.js.map +1 -0
- package/build/notify.js +393 -0
- package/build/notify.js.map +1 -0
- package/build/paths.js +1 -0
- package/build/paths.js.map +1 -1
- package/build/profile.js +7 -0
- package/build/profile.js.map +1 -1
- package/build/pushover.js +67 -0
- package/build/pushover.js.map +1 -0
- package/build/registry.js +3 -1
- package/build/registry.js.map +1 -1
- package/build/rule-input.js +17 -0
- package/build/rule-input.js.map +1 -0
- package/build/rules-service.js +391 -0
- package/build/rules-service.js.map +1 -0
- package/build/rules.js +842 -0
- package/build/rules.js.map +1 -0
- package/build/server.js +140 -0
- package/build/server.js.map +1 -1
- package/build/spend.js +8 -0
- package/build/spend.js.map +1 -1
- package/build/startup.js +30 -0
- package/build/startup.js.map +1 -1
- package/build/store.js +196 -0
- package/build/store.js.map +1 -1
- package/build/token.js +3 -3
- package/build/token.js.map +1 -1
- package/build/upgrade.js +105 -0
- package/build/upgrade.js.map +1 -0
- package/build/wait.js +32 -32
- package/build/wait.js.map +1 -1
- package/build/watchers/linear-key.js +6 -45
- package/build/watchers/linear-key.js.map +1 -1
- package/build/watchers/linear.js +16 -7
- package/build/watchers/linear.js.map +1 -1
- package/mod/.claude-plugin/marketplace.json +6 -0
- package/mod/.claude-plugin/plugin.json +7 -0
- package/mod/README.md +17 -0
- package/mod/hooks/hooks.json +1 -0
- package/mod/hooks/model.ts +395 -0
- package/mod/hooks/register.tsx +505 -0
- package/mod/types/index.d.ts +57 -0
- package/package.json +6 -1
package/build/notify.js
ADDED
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { parseLocalTime } from "./clock.js";
|
|
3
|
+
import { BUILT_IN_PROFILES, CHANNELS, DAYS } from "./config.js";
|
|
4
|
+
import { ConductorError } from "./errors.js";
|
|
5
|
+
import { pushMessage } from "./inbox.js";
|
|
6
|
+
import { NO_EXPIRY } from "./store.js";
|
|
7
|
+
export const PROFILE_OVERRIDE = 'notify.profile';
|
|
8
|
+
// An override's value that hands the profile back to config.json's `notify.profile`.
|
|
9
|
+
const CONFIG_PROFILE = '';
|
|
10
|
+
// A channel as the notifier last saw it work: Pushover by its own answers, Remote Control by what
|
|
11
|
+
// the conductor mod reports back from PushNotification.
|
|
12
|
+
export const channelStateSchema = z.object({
|
|
13
|
+
detail: z.string().nullable(),
|
|
14
|
+
since: z.string().nullable(),
|
|
15
|
+
// skipped: the last push wasn't sent to the phone on purpose (Jason was at the terminal).
|
|
16
|
+
state: z.enum(['failing', 'inactive', 'ok', 'skipped', 'unknown']),
|
|
17
|
+
});
|
|
18
|
+
// The states the conductor mod reports for a Remote Control push.
|
|
19
|
+
export const REPORTED_STATES = ['failing', 'inactive', 'ok', 'skipped'];
|
|
20
|
+
// GET /notify.
|
|
21
|
+
export const notifyStatusSchema = z.object({
|
|
22
|
+
channels: z.object({ pushover: channelStateSchema, 'remote-control': channelStateSchema }),
|
|
23
|
+
// Held by quiet hours, waiting for the summary.
|
|
24
|
+
held: z.number(),
|
|
25
|
+
profile: z.string(),
|
|
26
|
+
profiles: z.array(z.string()),
|
|
27
|
+
// `conduct notify profile` set it, until this time (null: until replaced); null when it's config's.
|
|
28
|
+
profileSetBy: z.enum(['config', 'conduct notify profile']),
|
|
29
|
+
profileUntil: z.string().nullable(),
|
|
30
|
+
quietHours: z.object({ from: z.string(), to: z.string() }).nullable(),
|
|
31
|
+
quietNow: z.boolean(),
|
|
32
|
+
});
|
|
33
|
+
// POST /notify/test.
|
|
34
|
+
export const testResultSchema = z.object({
|
|
35
|
+
channels: z.array(z.enum(CHANNELS)),
|
|
36
|
+
profile: z.string(),
|
|
37
|
+
quietNow: z.boolean(),
|
|
38
|
+
results: z.array(z.object({ channel: z.enum(CHANNELS), detail: z.string(), ok: z.boolean() })),
|
|
39
|
+
});
|
|
40
|
+
const raisedData = z.looseObject({ id: z.number() });
|
|
41
|
+
// True when the local time falls in quiet hours, which may run past midnight.
|
|
42
|
+
export function isQuiet(quietHours, at) {
|
|
43
|
+
return quietHours !== null && inRange(quietHours.from, quietHours.to, at);
|
|
44
|
+
}
|
|
45
|
+
// The notifier (design note, section 10): routes each new inbox item by the active profile's rules
|
|
46
|
+
// to Remote Control (a `notify.requested` event the conductor mod pushes) and Pushover (sent from
|
|
47
|
+
// here). Quiet hours hold pushes and send one summary when they end; a push of the same item, kind
|
|
48
|
+
// and text within dedupeMinutes is skipped; an open item is reminded until it resolves.
|
|
49
|
+
export function notifierService({ config, log, now = () => new Date(), pushover, store }) {
|
|
50
|
+
const { notify, quietHours } = config;
|
|
51
|
+
const profiles = { ...BUILT_IN_PROFILES, ...notify.profiles };
|
|
52
|
+
const channels = {
|
|
53
|
+
pushover: { detail: null, since: null, state: 'unknown' },
|
|
54
|
+
'remote-control': { detail: null, since: null, state: 'unknown' },
|
|
55
|
+
};
|
|
56
|
+
const inFlight = new Set();
|
|
57
|
+
let timer;
|
|
58
|
+
let ticking = null;
|
|
59
|
+
let lastProfile = null;
|
|
60
|
+
let stopped = false;
|
|
61
|
+
const track = (work) => {
|
|
62
|
+
inFlight.add(work);
|
|
63
|
+
void work.finally(() => inFlight.delete(work));
|
|
64
|
+
return work;
|
|
65
|
+
};
|
|
66
|
+
// Own names only: `constructor` or `toString` would otherwise pass as a profile.
|
|
67
|
+
const isProfile = (name) => Object.hasOwn(profiles, name);
|
|
68
|
+
const active = () => {
|
|
69
|
+
const override = store.liveOverride(PROFILE_OVERRIDE);
|
|
70
|
+
if (override && override.value !== CONFIG_PROFILE && isProfile(override.value)) {
|
|
71
|
+
return { name: override.value, setBy: 'conduct notify profile', until: override.until === NO_EXPIRY ? null : override.until };
|
|
72
|
+
}
|
|
73
|
+
return { name: notify.profile, setBy: 'config', until: null };
|
|
74
|
+
};
|
|
75
|
+
const route = (item, at) => {
|
|
76
|
+
const { name } = active();
|
|
77
|
+
const rules = isProfile(name) ? (profiles[name] ?? []) : [];
|
|
78
|
+
const index = rules.findIndex((r) => matches(r, item, at));
|
|
79
|
+
const rule = rules[index];
|
|
80
|
+
return rule ? { channels: unique(rule.to), remind: rule.remind ?? notify.remind, rule: index } : { channels: [], remind: notify.remind, rule: null };
|
|
81
|
+
};
|
|
82
|
+
const setChannel = (channel, state, detail) => {
|
|
83
|
+
const current = channels[channel];
|
|
84
|
+
if (current.state !== state) {
|
|
85
|
+
channels[channel] = { detail, since: now().toISOString(), state };
|
|
86
|
+
store.appendEvent({ data: { channel, detail, state }, kind: 'notify.channel' });
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
current.detail = detail;
|
|
90
|
+
}
|
|
91
|
+
return channels[channel];
|
|
92
|
+
};
|
|
93
|
+
// One push on each channel. Remote Control is a request the conductor mod carries out; Pushover
|
|
94
|
+
// goes out from here, and its answer is the event.
|
|
95
|
+
const deliver = async (to, message, extra = {}) => Promise.all(to.map(async (channel) => {
|
|
96
|
+
const base = { channel, inbox: message.inbox, kind: message.kind, ...extra };
|
|
97
|
+
if (channel === 'remote-control') {
|
|
98
|
+
store.appendEvent({ data: { ...base, message: message.text }, kind: 'notify.requested', project: message.project, subject: message.item });
|
|
99
|
+
return { channel, result: { ok: true } };
|
|
100
|
+
}
|
|
101
|
+
const result = await track(pushover({
|
|
102
|
+
message: [message.text, ...message.commands.map((c) => `$ ${c}`)].join('\n'),
|
|
103
|
+
priority: notify.pushover.priority,
|
|
104
|
+
title: message.title,
|
|
105
|
+
...(message.link ? { url: message.link, urlTitle: 'open' } : {}),
|
|
106
|
+
}));
|
|
107
|
+
if (result.ok) {
|
|
108
|
+
setChannel(channel, 'ok', null);
|
|
109
|
+
store.appendEvent({ data: base, kind: 'notify.sent', project: message.project, subject: message.item });
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
setChannel(channel, 'failing', result.error);
|
|
113
|
+
log.log('warn', 'a Pushover push failed', { error: result.error, inbox: message.inbox });
|
|
114
|
+
store.appendEvent({ data: { ...base, error: result.error }, kind: 'notify.failed', project: message.project, subject: message.item });
|
|
115
|
+
}
|
|
116
|
+
return { channel, result };
|
|
117
|
+
}));
|
|
118
|
+
const onRaised = async (event) => {
|
|
119
|
+
const data = raisedData.safeParse(event.data);
|
|
120
|
+
if (!data.success)
|
|
121
|
+
return;
|
|
122
|
+
const item = store.getInbox(data.data.id);
|
|
123
|
+
if (!item || store.getNotifyState(item.id))
|
|
124
|
+
return;
|
|
125
|
+
const at = now();
|
|
126
|
+
const routed = route(item, at);
|
|
127
|
+
const state = {
|
|
128
|
+
channels: routed.channels,
|
|
129
|
+
dedupeKey: JSON.stringify([item.project, item.item?.toUpperCase() ?? null, item.kind, item.text]),
|
|
130
|
+
heldAt: null,
|
|
131
|
+
inbox: item.id,
|
|
132
|
+
lastSentAt: null,
|
|
133
|
+
remindAfterMinutes: routed.remind.afterMinutes,
|
|
134
|
+
remindTimes: routed.remind.times,
|
|
135
|
+
sends: 0,
|
|
136
|
+
};
|
|
137
|
+
const earlier = notify.dedupeMinutes > 0 ? store.lastNotified(state.dedupeKey, new Date(at.getTime() - notify.dedupeMinutes * 60_000).toISOString()) : null;
|
|
138
|
+
// An earlier push stands for this one only where it went: after a switch to away, the same ask
|
|
139
|
+
// still goes to Pushover.
|
|
140
|
+
const prior = earlier && routed.channels.every((c) => earlier.channels.includes(c)) ? earlier : null;
|
|
141
|
+
const outcome = routed.channels.length === 0 ? 'none'
|
|
142
|
+
: isQuiet(quietHours, at) ? 'held'
|
|
143
|
+
: prior ? 'deduped'
|
|
144
|
+
: 'sent';
|
|
145
|
+
if (outcome === 'held')
|
|
146
|
+
state.heldAt = at.toISOString();
|
|
147
|
+
if (outcome === 'held' || outcome === 'sent') {
|
|
148
|
+
// An older copy that never reached the phone (held for the summary, or a first push that
|
|
149
|
+
// failed) gives way to this one, so the ask is summarized or tried again once.
|
|
150
|
+
for (const older of store.liveNotifyStates()) {
|
|
151
|
+
const unsent = older.heldAt !== null || (older.sends === 0 && older.lastSentAt !== null);
|
|
152
|
+
if (older.dedupeKey === state.dedupeKey && unsent)
|
|
153
|
+
store.saveNotifyState({ ...older, channels: [], heldAt: null });
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (outcome === 'deduped' && prior) {
|
|
157
|
+
// The earlier push stands for this one, and its reminders carry on here alone: the earlier
|
|
158
|
+
// item, if still open, would otherwise remind too, each reminder arriving twice.
|
|
159
|
+
state.sends = prior.sends;
|
|
160
|
+
state.lastSentAt = prior.lastSentAt;
|
|
161
|
+
store.saveNotifyState({ ...prior, channels: [] });
|
|
162
|
+
}
|
|
163
|
+
if (outcome === 'sent') {
|
|
164
|
+
state.sends = 1;
|
|
165
|
+
state.lastSentAt = at.toISOString();
|
|
166
|
+
}
|
|
167
|
+
store.saveNotifyState(state);
|
|
168
|
+
store.appendEvent({
|
|
169
|
+
data: { channels: routed.channels, inbox: item.id, outcome, profile: active().name, rule: routed.rule, ...(prior && outcome === 'deduped' ? { dedupedOf: prior.inbox } : {}) },
|
|
170
|
+
kind: 'notify.routed',
|
|
171
|
+
project: item.project,
|
|
172
|
+
subject: item.item,
|
|
173
|
+
});
|
|
174
|
+
if (outcome !== 'sent')
|
|
175
|
+
return;
|
|
176
|
+
const results = await deliver(routed.channels, messageOf(item));
|
|
177
|
+
// Nothing arrived: it isn't a push to dedupe against, and the reminder pass tries it again.
|
|
178
|
+
if (results.every((r) => !r.result.ok))
|
|
179
|
+
store.saveNotifyState({ ...state, sends: 0 });
|
|
180
|
+
};
|
|
181
|
+
// Quiet hours are over: one push per channel naming the held items still open, which are then
|
|
182
|
+
// reminded as if pushed now.
|
|
183
|
+
const summarize = async (held, at) => {
|
|
184
|
+
const byChannel = new Map();
|
|
185
|
+
let resolved = 0;
|
|
186
|
+
for (const state of held) {
|
|
187
|
+
const item = store.getInbox(state.inbox);
|
|
188
|
+
if (!item || item.resolvedAt !== null) {
|
|
189
|
+
resolved++;
|
|
190
|
+
store.saveNotifyState({ ...state, heldAt: null });
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
const routed = route(item, at);
|
|
194
|
+
for (const channel of routed.channels)
|
|
195
|
+
byChannel.set(channel, [...(byChannel.get(channel) ?? []), item]);
|
|
196
|
+
store.saveNotifyState({
|
|
197
|
+
...state,
|
|
198
|
+
channels: routed.channels,
|
|
199
|
+
heldAt: null,
|
|
200
|
+
lastSentAt: routed.channels.length > 0 ? at.toISOString() : state.lastSentAt,
|
|
201
|
+
remindAfterMinutes: routed.remind.afterMinutes,
|
|
202
|
+
remindTimes: routed.remind.times,
|
|
203
|
+
sends: routed.channels.length > 0 ? 1 : state.sends,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
await Promise.all([...byChannel.entries()].map(([channel, items]) => {
|
|
207
|
+
const text = summaryText(items, resolved);
|
|
208
|
+
return deliver([channel], { commands: [], inbox: null, item: null, kind: 'summary', link: null, project: null, text, title: 'conductor · since quiet hours' }, { held: items.map((i) => i.id), resolved });
|
|
209
|
+
}));
|
|
210
|
+
};
|
|
211
|
+
// Reminders, on the channels the profile in force now gives the item. A first push that reached
|
|
212
|
+
// no channel (sends 0) is tried again at the same interval, without using up a reminder.
|
|
213
|
+
const remind = async (states, at) => {
|
|
214
|
+
for (const state of states) {
|
|
215
|
+
if (state.heldAt !== null || state.lastSentAt === null || state.channels.length === 0)
|
|
216
|
+
continue;
|
|
217
|
+
const retry = state.sends === 0;
|
|
218
|
+
const reminder = state.sends; // the first push is send 1, so reminder n follows send n
|
|
219
|
+
if (!retry && reminder > state.remindTimes)
|
|
220
|
+
continue;
|
|
221
|
+
if (at.getTime() - Date.parse(state.lastSentAt) < state.remindAfterMinutes * 60_000)
|
|
222
|
+
continue;
|
|
223
|
+
const item = store.getInbox(state.inbox);
|
|
224
|
+
if (!item || item.resolvedAt !== null)
|
|
225
|
+
continue;
|
|
226
|
+
const to = route(item, at).channels;
|
|
227
|
+
if (to.length === 0)
|
|
228
|
+
continue;
|
|
229
|
+
const message = messageOf(item);
|
|
230
|
+
const prefix = `reminder ${reminder}/${state.remindTimes}: `;
|
|
231
|
+
const results = await deliver(to, retry ? message : { ...message, text: pushMessage({ item: item.item, text: `${prefix}${item.text}` }), title: `${prefix}${message.title}` }, retry ? { retry: true } : { reminder });
|
|
232
|
+
const arrived = results.some((r) => r.result.ok);
|
|
233
|
+
store.saveNotifyState({ ...state, channels: to, lastSentAt: at.toISOString(), sends: retry && !arrived ? 0 : state.sends + 1 });
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
const tick = async () => {
|
|
237
|
+
const at = now();
|
|
238
|
+
const profile = active();
|
|
239
|
+
if (lastProfile !== null && lastProfile !== profile.name) {
|
|
240
|
+
store.appendEvent({ data: { from: lastProfile, profile: profile.name, reason: 'the timed profile ran out' }, kind: 'notify.profile' });
|
|
241
|
+
}
|
|
242
|
+
lastProfile = profile.name;
|
|
243
|
+
if (isQuiet(quietHours, at))
|
|
244
|
+
return;
|
|
245
|
+
const live = store.liveNotifyStates();
|
|
246
|
+
const held = live.filter((s) => s.heldAt !== null);
|
|
247
|
+
if (held.length > 0)
|
|
248
|
+
await summarize(held, at);
|
|
249
|
+
await remind(live.filter((s) => s.heldAt === null), at);
|
|
250
|
+
};
|
|
251
|
+
const status = () => {
|
|
252
|
+
const profile = active();
|
|
253
|
+
return {
|
|
254
|
+
channels: { pushover: { ...channels.pushover }, 'remote-control': { ...channels['remote-control'] } },
|
|
255
|
+
held: store.liveNotifyStates().filter((s) => s.heldAt !== null).length,
|
|
256
|
+
profile: profile.name,
|
|
257
|
+
profiles: Object.keys(profiles).sort(),
|
|
258
|
+
profileSetBy: profile.setBy,
|
|
259
|
+
profileUntil: profile.until,
|
|
260
|
+
quietHours,
|
|
261
|
+
quietNow: isQuiet(quietHours, now()),
|
|
262
|
+
};
|
|
263
|
+
};
|
|
264
|
+
return {
|
|
265
|
+
// Nothing after stop: the store closes behind it.
|
|
266
|
+
observe: (event) => (event.kind === 'inbox.raised' && !stopped ? track(onRaised(event)) : Promise.resolve()),
|
|
267
|
+
report: (channel, result, state) => {
|
|
268
|
+
// PushNotification says "Mobile push not sent (Remote Control inactive)" when no phone is connected.
|
|
269
|
+
const seen = state ?? (/remote control inactive/i.test(result) ? 'inactive' : 'ok');
|
|
270
|
+
return setChannel(channel, seen, seen === 'ok' ? null : result);
|
|
271
|
+
},
|
|
272
|
+
setProfile: (name, until) => {
|
|
273
|
+
if (!isProfile(name)) {
|
|
274
|
+
throw new ConductorError(`no notification profile "${name}"`, [`profiles: ${Object.keys(profiles).sort().join(', ')}`]);
|
|
275
|
+
}
|
|
276
|
+
if (until !== null && until.getTime() <= now().getTime())
|
|
277
|
+
throw new ConductorError(`--until ${until.toISOString()} is in the past`);
|
|
278
|
+
const from = active().name;
|
|
279
|
+
// Config's own profile with no end hands the choice back to config.json.
|
|
280
|
+
const value = until === null && name === notify.profile ? CONFIG_PROFILE : name;
|
|
281
|
+
store.setOverride({ kind: PROFILE_OVERRIDE, project: null, until: until?.toISOString() ?? NO_EXPIRY, value });
|
|
282
|
+
lastProfile = name;
|
|
283
|
+
store.appendEvent({ data: { from, profile: name, until: until?.toISOString() ?? null }, kind: 'notify.profile' });
|
|
284
|
+
return status();
|
|
285
|
+
},
|
|
286
|
+
start: () => {
|
|
287
|
+
lastProfile = active().name;
|
|
288
|
+
timer = setInterval(() => {
|
|
289
|
+
if (ticking)
|
|
290
|
+
return;
|
|
291
|
+
ticking = track(tick()
|
|
292
|
+
.catch((error) => log.log('error', 'the notifier tick failed', { error: String(error) }))
|
|
293
|
+
.finally(() => (ticking = null)));
|
|
294
|
+
}, notify.tickSeconds * 1000);
|
|
295
|
+
timer.unref();
|
|
296
|
+
},
|
|
297
|
+
status,
|
|
298
|
+
stop: async () => {
|
|
299
|
+
stopped = true;
|
|
300
|
+
clearInterval(timer);
|
|
301
|
+
await Promise.allSettled([...inFlight]);
|
|
302
|
+
},
|
|
303
|
+
test: async (kind, channel) => {
|
|
304
|
+
const at = now();
|
|
305
|
+
const sample = { kind, project: null };
|
|
306
|
+
const to = channel ? [channel] : route(sample, at).channels;
|
|
307
|
+
const text = `test: a sample ${kind} item (conduct notify test)`;
|
|
308
|
+
const sent = await deliver(to, { commands: [], inbox: null, item: null, kind, link: null, project: null, text, title: `conductor · test ${kind}` }, { test: true });
|
|
309
|
+
return {
|
|
310
|
+
channels: to,
|
|
311
|
+
profile: active().name,
|
|
312
|
+
quietNow: isQuiet(quietHours, at),
|
|
313
|
+
results: sent.map(({ channel: c, result }) => ({
|
|
314
|
+
channel: c,
|
|
315
|
+
detail: result.ok ? (c === 'remote-control' ? 'requested: the conductor mod in the console pushes it' : 'sent') : result.error,
|
|
316
|
+
ok: result.ok,
|
|
317
|
+
})),
|
|
318
|
+
};
|
|
319
|
+
},
|
|
320
|
+
tick,
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
// `conduct notify`: the profile, quiet hours and each channel's state.
|
|
324
|
+
export function notifyLines(status) {
|
|
325
|
+
const until = status.profileUntil === null ? '' : ` until ${new Date(status.profileUntil).toLocaleString()}`;
|
|
326
|
+
const quiet = status.quietHours === null ? 'none' : `${status.quietHours.from}-${status.quietHours.to}${status.quietNow ? ' (now)' : ''}`;
|
|
327
|
+
const channel = (name) => {
|
|
328
|
+
const c = status.channels[name];
|
|
329
|
+
const since = c.since === null ? '' : ` since ${new Date(c.since).toLocaleString()}`;
|
|
330
|
+
return ` ${name}: ${c.state}${since}${c.detail ? ` (${c.detail})` : ''}`;
|
|
331
|
+
};
|
|
332
|
+
return [
|
|
333
|
+
`profile: ${status.profile}${until} (set by ${status.profileSetBy}; profiles: ${status.profiles.join(', ')})`,
|
|
334
|
+
`quiet hours: ${quiet}${status.held > 0 ? `; ${status.held} held for the summary` : ''}`,
|
|
335
|
+
'channels:',
|
|
336
|
+
channel('remote-control'),
|
|
337
|
+
channel('pushover'),
|
|
338
|
+
];
|
|
339
|
+
}
|
|
340
|
+
// `conduct notify profile … --until`: a local time of day (the next one), or a date and time.
|
|
341
|
+
export function parseUntil(text, now) {
|
|
342
|
+
const at = parseLocalTime(text, now);
|
|
343
|
+
if (!at) {
|
|
344
|
+
throw new ConductorError(`--until "${text}" is not a time`, ['a local time such as 17:00 (the next one), or a date and time such as 2026-10-12T09:00']);
|
|
345
|
+
}
|
|
346
|
+
return at;
|
|
347
|
+
}
|
|
348
|
+
function inRange(from, to, at) {
|
|
349
|
+
const minute = at.getHours() * 60 + at.getMinutes();
|
|
350
|
+
const start = minutesOf(from);
|
|
351
|
+
const end = minutesOf(to);
|
|
352
|
+
return start < end ? minute >= start && minute < end : minute >= start || minute < end;
|
|
353
|
+
}
|
|
354
|
+
function matches(rule, item, at) {
|
|
355
|
+
if (rule.kinds && !rule.kinds.includes(item.kind))
|
|
356
|
+
return false;
|
|
357
|
+
if (rule.projects && (item.project === null || !rule.projects.includes(item.project)))
|
|
358
|
+
return false;
|
|
359
|
+
if (rule.days && !rule.days.includes(DAYS[at.getDay()] ?? 'sun'))
|
|
360
|
+
return false;
|
|
361
|
+
if (rule.hours) {
|
|
362
|
+
const [from = '', to = ''] = rule.hours.split('-');
|
|
363
|
+
if (!inRange(from, to, at))
|
|
364
|
+
return false;
|
|
365
|
+
}
|
|
366
|
+
return true;
|
|
367
|
+
}
|
|
368
|
+
function messageOf(item) {
|
|
369
|
+
return {
|
|
370
|
+
commands: item.commands,
|
|
371
|
+
inbox: item.id,
|
|
372
|
+
item: item.item,
|
|
373
|
+
kind: item.kind,
|
|
374
|
+
link: item.links[0] ?? null,
|
|
375
|
+
project: item.project,
|
|
376
|
+
text: pushMessage(item),
|
|
377
|
+
title: `conductor · ${[item.item, item.kind].filter((x) => x !== null).join(' ')}`,
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
function minutesOf(clock) {
|
|
381
|
+
const [hours = 0, minutes = 0] = clock.split(':').map(Number);
|
|
382
|
+
return hours * 60 + minutes;
|
|
383
|
+
}
|
|
384
|
+
// "3 waiting since quiet hours: ATK-1 plan, ATK-2 npm-otp, #7 failed; 1 resolved meanwhile".
|
|
385
|
+
function summaryText(items, resolved) {
|
|
386
|
+
const names = items.map((i) => `${i.item ?? `#${i.id}`} ${i.kind}`).join(', ');
|
|
387
|
+
const text = `${items.length} waiting since quiet hours: ${names}${resolved > 0 ? `; ${resolved} resolved meanwhile` : ''}`;
|
|
388
|
+
return pushMessage({ item: null, text });
|
|
389
|
+
}
|
|
390
|
+
function unique(values) {
|
|
391
|
+
return [...new Set(values)];
|
|
392
|
+
}
|
|
393
|
+
//# sourceMappingURL=notify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notify.js","sourceRoot":"","sources":["../src/notify.ts"],"names":[],"mappings":"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"}
|
package/build/paths.js
CHANGED
package/build/paths.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAiBjC,MAAM,UAAU,cAAc,CAAC,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,YAAY,CAAC;IAC/F,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAChC,OAAO;QACL,MAAM,EAAE,IAAI,CAAC,IAAI,EAAE,aAAa,CAAC;QACjC,EAAE,EAAE,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC;QAC9B,IAAI;QACJ,GAAG,EAAE,IAAI,CAAC,IAAI,EAAE,eAAe,CAAC;QAChC,IAAI;QACJ,GAAG,EAAE,IAAI,CAAC,IAAI,EAAE,eAAe,CAAC;QAChC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC;QACxB,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE,YAAY,CAAC;QAC/B,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC;KAC3B,CAAC;AACJ,CAAC"}
|
package/build/profile.js
CHANGED
|
@@ -3,6 +3,7 @@ import { join } from 'node:path';
|
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
import { ConductorError, isMissing } from "./errors.js";
|
|
5
5
|
import { parseJsonc } from "./jsonc.js";
|
|
6
|
+
import { ruleInputSchema } from "./rule-input.js";
|
|
6
7
|
// The worker protocol this conductor speaks. A profile pins the version its skills use, so repos on
|
|
7
8
|
// different skill versions can be conducted side by side (design note, section 3).
|
|
8
9
|
export const PROTOCOL = 1;
|
|
@@ -28,6 +29,9 @@ export const profileSchema = z.strictObject({
|
|
|
28
29
|
ports: z.strictObject({ api: port, maxSlot: z.int().min(0).max(1000), web: port }),
|
|
29
30
|
protocol: z.literal(PROTOCOL),
|
|
30
31
|
release: z.strictObject({ handoff: text }),
|
|
32
|
+
// Project rules (design note, section 9): checked before the machine's and the defaults, for
|
|
33
|
+
// this project's items only. They change by PR, and the daemon reloads them on save.
|
|
34
|
+
rules: z.array(ruleInputSchema).default([]),
|
|
31
35
|
skills: z.strictObject({ verify: text, work: text }),
|
|
32
36
|
slots: z.int().min(1).max(8),
|
|
33
37
|
tracker: z.discriminatedUnion('kind', [
|
|
@@ -40,6 +44,9 @@ export const profileSchema = z.strictObject({
|
|
|
40
44
|
team: text,
|
|
41
45
|
}),
|
|
42
46
|
]),
|
|
47
|
+
// Orders this project's queue against other projects' of the same tracker priority: higher
|
|
48
|
+
// first. `conduct focus --weight` overrides it for a while.
|
|
49
|
+
weight: z.int().min(-100).max(100).default(0),
|
|
43
50
|
// How the daemon starts this project's workers. `tab` opens each in a Windows Terminal tab
|
|
44
51
|
// attached to its background session.
|
|
45
52
|
workers: z.strictObject({ tab: z.boolean().default(false) }).prefault({}),
|
package/build/profile.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"profile.js","sourceRoot":"","sources":["../src/profile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"profile.js","sourceRoot":"","sources":["../src/profile.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { readCredential as readWindowsCredential } from "./credential.js";
|
|
2
|
+
const ENDPOINT = 'https://api.pushover.net/1/messages.json';
|
|
3
|
+
// Sends from the daemon. The keys come from PUSHOVER_TOKEN and PUSHOVER_USER, else the credential,
|
|
4
|
+
// read when a message first goes out and again after Pushover refuses them, so a credential saved
|
|
5
|
+
// while the daemon runs is picked up. They are never logged, stored or put in an error.
|
|
6
|
+
export function pushoverSender({ credential, env = process.env, fetch: fetchImpl = fetch, readCredential = readWindowsCredential, timeoutMs = 15_000, }) {
|
|
7
|
+
let cached = null;
|
|
8
|
+
// One Credential Manager read at a time: pushes that start together share it.
|
|
9
|
+
let reading = null;
|
|
10
|
+
const read = async () => {
|
|
11
|
+
const found = await readCredential(credential, env);
|
|
12
|
+
if (!found.found)
|
|
13
|
+
return { error: `no PUSHOVER_TOKEN and PUSHOVER_USER, and ${found.reason}` };
|
|
14
|
+
if (!found.credential.user)
|
|
15
|
+
return { error: `the credential ${credential} has no user name: save the Pushover user key as its user name` };
|
|
16
|
+
return { keys: (cached = { token: found.credential.secret, user: found.credential.user }) };
|
|
17
|
+
};
|
|
18
|
+
const keys = async () => {
|
|
19
|
+
if (cached)
|
|
20
|
+
return { keys: cached };
|
|
21
|
+
const token = env.PUSHOVER_TOKEN?.trim();
|
|
22
|
+
const user = env.PUSHOVER_USER?.trim();
|
|
23
|
+
if (token && user)
|
|
24
|
+
return { keys: (cached = { token, user }) };
|
|
25
|
+
reading ??= read().finally(() => (reading = null));
|
|
26
|
+
return reading;
|
|
27
|
+
};
|
|
28
|
+
return async (message) => {
|
|
29
|
+
const found = await keys();
|
|
30
|
+
if ('error' in found)
|
|
31
|
+
return { error: found.error, ok: false };
|
|
32
|
+
const form = new URLSearchParams({
|
|
33
|
+
message: cut(message.message, 1024),
|
|
34
|
+
priority: String(message.priority),
|
|
35
|
+
title: cut(message.title, 250),
|
|
36
|
+
token: found.keys.token,
|
|
37
|
+
user: found.keys.user,
|
|
38
|
+
});
|
|
39
|
+
if (message.url && message.url.length <= 512) {
|
|
40
|
+
form.set('url', message.url);
|
|
41
|
+
if (message.urlTitle)
|
|
42
|
+
form.set('url_title', cut(message.urlTitle, 100));
|
|
43
|
+
}
|
|
44
|
+
let response;
|
|
45
|
+
try {
|
|
46
|
+
response = await fetchImpl(ENDPOINT, { body: form, method: 'POST', signal: AbortSignal.timeout(timeoutMs) });
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
return { error: `Pushover didn't answer: ${error instanceof Error ? error.message : String(error)}`, ok: false };
|
|
50
|
+
}
|
|
51
|
+
const body = await response.json().catch(() => null);
|
|
52
|
+
if (response.ok && isRecord(body) && body.status === 1)
|
|
53
|
+
return { ok: true };
|
|
54
|
+
// 4xx: Pushover refused the request, often the keys; read them again next time.
|
|
55
|
+
if (response.status >= 400 && response.status < 500)
|
|
56
|
+
cached = null;
|
|
57
|
+
const errors = isRecord(body) && Array.isArray(body.errors) ? body.errors.filter((e) => typeof e === 'string') : [];
|
|
58
|
+
return { error: `Pushover answered ${response.status}${errors.length > 0 ? `: ${errors.join('; ')}` : ''}`, ok: false };
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function cut(text, limit) {
|
|
62
|
+
return text.length <= limit ? text : `${text.slice(0, limit - 1)}…`;
|
|
63
|
+
}
|
|
64
|
+
function isRecord(value) {
|
|
65
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=pushover.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pushover.js","sourceRoot":"","sources":["../src/pushover.ts"],"names":[],"mappings":"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"}
|
package/build/registry.js
CHANGED
|
@@ -25,9 +25,11 @@ export function projectRoot(path) {
|
|
|
25
25
|
return start;
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
|
-
|
|
28
|
+
// `check` refuses a profile before it is stored: the daemon checks its rules against config.json.
|
|
29
|
+
export function registerProject(store, path, check = () => { }) {
|
|
29
30
|
const root = projectRoot(path);
|
|
30
31
|
const { path: profilePath, profile } = readProfile(root);
|
|
32
|
+
check(profile, profilePath);
|
|
31
33
|
return store.upsertProject({ profile, profilePath, root });
|
|
32
34
|
}
|
|
33
35
|
//# sourceMappingURL=registry.js.map
|
package/build/registry.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAI7D,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAI7D,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAgB,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEvE,+FAA+F;AAC/F,sFAAsF;AACtF,mGAAmG;AACnG,mGAAmG;AACnG,mFAAmF;AACnF,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,IAAI,KAAa,CAAC;IAClB,IAAI,CAAC;QACH,KAAK,GAAG,YAAY,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,cAAc,CAAC,GAAG,IAAI,iBAAiB,CAAC,CAAC;IACrD,CAAC;IACD,KAAK,IAAI,GAAG,GAAG,KAAK,GAAI,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QAC3C,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,QAAQ,CAAC,MAAM,CAAC,KAAK,WAAW,IAAI,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,SAAS;YAAE,OAAO,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;QACjH,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;YAAE,OAAO,GAAG,CAAC;QACpD,IAAI,MAAM,KAAK,GAAG;YAAE,OAAO,KAAK,CAAC;IACnC,CAAC;AACH,CAAC;AAED,kGAAkG;AAClG,MAAM,UAAU,eAAe,CAC7B,KAAY,EACZ,IAAY,EACZ,QAAkD,GAAG,EAAE,GAAE,CAAC;IAE1D,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;IACzD,KAAK,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IAC5B,OAAO,KAAK,CAAC,aAAa,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;AAC7D,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
// A rule as written in ~/.conductor/rules.json or a profile's `rules` (design note, section 9):
|
|
3
|
+
// `when` and `do` in the fixed vocabulary that rules.ts reads. Its own module, so profile.ts can
|
|
4
|
+
// take rules without importing the engine (which imports the store, which imports profile.ts).
|
|
5
|
+
export const ruleIdSchema = z.string().regex(/^[a-z][a-z0-9-]*$/, 'lowercase letters, digits and dashes, starting with a letter');
|
|
6
|
+
export const ruleInputSchema = z.strictObject({
|
|
7
|
+
// One action, or several that fire together.
|
|
8
|
+
do: z.union([z.string().trim().min(1), z.array(z.string().trim().min(1)).min(1)]),
|
|
9
|
+
// A rule with the id of one further down (a machine rule over a default, a project rule over
|
|
10
|
+
// either) replaces it, so a file holds only the differences. `do: "none"` switches one off.
|
|
11
|
+
id: ruleIdSchema.optional(),
|
|
12
|
+
note: z.string().optional(),
|
|
13
|
+
// Left out only by a `do: "none"` rule, which needs no condition to switch a rule off.
|
|
14
|
+
when: z.string().trim().min(1).optional(),
|
|
15
|
+
});
|
|
16
|
+
export const rulesFileSchema = z.array(ruleInputSchema);
|
|
17
|
+
//# sourceMappingURL=rule-input.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rule-input.js","sourceRoot":"","sources":["../src/rule-input.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,gGAAgG;AAChG,iGAAiG;AACjG,+FAA+F;AAC/F,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,mBAAmB,EAAE,8DAA8D,CAAC,CAAC;AAElI,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,YAAY,CAAC;IAC5C,6CAA6C;IAC7C,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACjF,6FAA6F;IAC7F,4FAA4F;IAC5F,EAAE,EAAE,YAAY,CAAC,QAAQ,EAAE;IAC3B,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC3B,uFAAuF;IACvF,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;CAC1C,CAAC,CAAC;AAIH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC"}
|