@hwip/cli 0.0.1 → 0.1.0
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/LICENSE +190 -0
- package/NOTICE +11 -0
- package/README.md +1979 -0
- package/dist/agent-list.d.ts +76 -0
- package/dist/agent-list.js +102 -0
- package/dist/agent-read.d.ts +105 -0
- package/dist/agent-read.js +309 -0
- package/dist/agent-rules-fetch.d.ts +51 -0
- package/dist/agent-rules-fetch.js +275 -0
- package/dist/agent-rules.default.d.ts +30 -0
- package/dist/agent-rules.default.js +292 -0
- package/dist/agent-session.d.ts +23 -0
- package/dist/agent-session.js +44 -0
- package/dist/agent-state.d.ts +84 -0
- package/dist/agent-state.js +188 -0
- package/dist/agent-target.d.ts +60 -0
- package/dist/agent-target.js +81 -0
- package/dist/agents.d.ts +31 -0
- package/dist/agents.js +75 -0
- package/dist/api.d.ts +28 -0
- package/dist/api.js +76 -0
- package/dist/args.d.ts +74 -0
- package/dist/args.js +254 -0
- package/dist/ask-context-attach.d.ts +73 -0
- package/dist/ask-context-attach.js +151 -0
- package/dist/ask-context-screen.d.ts +21 -0
- package/dist/ask-context-screen.js +49 -0
- package/dist/ask-context-switch.d.ts +31 -0
- package/dist/ask-context-switch.js +74 -0
- package/dist/ask-context.d.ts +84 -0
- package/dist/ask-context.js +172 -0
- package/dist/ask-pending.d.ts +47 -0
- package/dist/ask-pending.js +221 -0
- package/dist/ask-seal.d.ts +120 -0
- package/dist/ask-seal.js +179 -0
- package/dist/ask.d.ts +107 -0
- package/dist/ask.js +551 -0
- package/dist/blocked-prompt-seal.d.ts +35 -0
- package/dist/blocked-prompt-seal.js +89 -0
- package/dist/blocked-prompt.d.ts +57 -0
- package/dist/blocked-prompt.js +181 -0
- package/dist/channel.d.ts +67 -0
- package/dist/channel.js +136 -0
- package/dist/check-update.d.ts +28 -0
- package/dist/check-update.js +153 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +813 -0
- package/dist/codex-config.d.ts +24 -0
- package/dist/codex-config.js +169 -0
- package/dist/codex-transcript.d.ts +59 -0
- package/dist/codex-transcript.js +373 -0
- package/dist/command-refusal.d.ts +56 -0
- package/dist/command-refusal.js +127 -0
- package/dist/command-scan.d.ts +46 -0
- package/dist/command-scan.js +154 -0
- package/dist/command-seal.d.ts +57 -0
- package/dist/command-seal.js +88 -0
- package/dist/command-seq.d.ts +30 -0
- package/dist/command-seq.js +150 -0
- package/dist/config.d.ts +156 -0
- package/dist/config.js +392 -0
- package/dist/core-sections.d.ts +18 -0
- package/dist/core-sections.js +28 -0
- package/dist/crypto.d.ts +278 -0
- package/dist/crypto.js +698 -0
- package/dist/desktop-notify.d.ts +33 -0
- package/dist/desktop-notify.js +49 -0
- package/dist/device-directory.d.ts +36 -0
- package/dist/device-directory.js +97 -0
- package/dist/device-presence.d.ts +17 -0
- package/dist/device-presence.js +62 -0
- package/dist/downloads.d.ts +29 -0
- package/dist/downloads.js +106 -0
- package/dist/errors.d.ts +12 -0
- package/dist/errors.js +28 -0
- package/dist/file-crypto.d.ts +94 -0
- package/dist/file-crypto.js +272 -0
- package/dist/file-view-budget.d.ts +15 -0
- package/dist/file-view-budget.js +34 -0
- package/dist/file-view-switch.d.ts +28 -0
- package/dist/file-view-switch.js +122 -0
- package/dist/file-view.d.ts +108 -0
- package/dist/file-view.js +327 -0
- package/dist/gate.d.ts +141 -0
- package/dist/gate.js +336 -0
- package/dist/gemini-transcript.d.ts +32 -0
- package/dist/gemini-transcript.js +189 -0
- package/dist/http-failure.d.ts +98 -0
- package/dist/http-failure.js +349 -0
- package/dist/hwip-core.generated.d.ts +7 -0
- package/dist/hwip-core.generated.js +14 -0
- package/dist/hwip-hook.d.ts +77 -0
- package/dist/hwip-hook.js +340 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +14 -0
- package/dist/input-sequencer.d.ts +65 -0
- package/dist/input-sequencer.js +142 -0
- package/dist/insecure-url.d.ts +16 -0
- package/dist/insecure-url.js +76 -0
- package/dist/installer.d.ts +96 -0
- package/dist/installer.js +850 -0
- package/dist/inventory-offload.d.ts +49 -0
- package/dist/inventory-offload.js +209 -0
- package/dist/inventory-worker.d.ts +2 -0
- package/dist/inventory-worker.js +27 -0
- package/dist/jsonc-doc.d.ts +106 -0
- package/dist/jsonc-doc.js +354 -0
- package/dist/keep-awake.d.ts +32 -0
- package/dist/keep-awake.js +46 -0
- package/dist/key-changes.d.ts +48 -0
- package/dist/key-changes.js +140 -0
- package/dist/lan-auth.d.ts +140 -0
- package/dist/lan-auth.js +217 -0
- package/dist/lan-endpoint.d.ts +41 -0
- package/dist/lan-endpoint.js +115 -0
- package/dist/lan-receiver.d.ts +122 -0
- package/dist/lan-receiver.js +510 -0
- package/dist/lan-sender.d.ts +76 -0
- package/dist/lan-sender.js +184 -0
- package/dist/lan-transfer.d.ts +36 -0
- package/dist/lan-transfer.js +54 -0
- package/dist/listener-device-id.d.ts +20 -0
- package/dist/listener-device-id.js +133 -0
- package/dist/listener-log.d.ts +4 -0
- package/dist/listener-log.js +23 -0
- package/dist/listener-process.d.ts +200 -0
- package/dist/listener-process.js +470 -0
- package/dist/listener-service.d.ts +225 -0
- package/dist/listener-service.js +682 -0
- package/dist/listener-worktree-cleanup.d.ts +27 -0
- package/dist/listener-worktree-cleanup.js +62 -0
- package/dist/listener.d.ts +1325 -0
- package/dist/listener.js +5680 -0
- package/dist/login-address.d.ts +25 -0
- package/dist/login-address.js +97 -0
- package/dist/login-page.d.ts +29 -0
- package/dist/login-page.js +93 -0
- package/dist/login.d.ts +98 -0
- package/dist/login.js +388 -0
- package/dist/mcp-command.d.ts +45 -0
- package/dist/mcp-command.js +50 -0
- package/dist/mcp.d.ts +19 -0
- package/dist/mcp.js +29 -0
- package/dist/node-support.d.ts +19 -0
- package/dist/node-support.js +38 -0
- package/dist/notify-failure.d.ts +57 -0
- package/dist/notify-failure.js +150 -0
- package/dist/opencode-transcript.d.ts +139 -0
- package/dist/opencode-transcript.js +390 -0
- package/dist/optional-features.d.ts +6 -0
- package/dist/optional-features.js +19 -0
- package/dist/pane-fit.d.ts +40 -0
- package/dist/pane-fit.js +119 -0
- package/dist/peer-key.d.ts +33 -0
- package/dist/peer-key.js +34 -0
- package/dist/pi-transcript.d.ts +26 -0
- package/dist/pi-transcript.js +255 -0
- package/dist/presence.d.ts +14 -0
- package/dist/presence.js +124 -0
- package/dist/private-files.d.ts +51 -0
- package/dist/private-files.js +218 -0
- package/dist/project-switch.d.ts +19 -0
- package/dist/project-switch.js +60 -0
- package/dist/redact.d.ts +10 -0
- package/dist/redact.js +21 -0
- package/dist/remote-agents.d.ts +410 -0
- package/dist/remote-agents.js +1218 -0
- package/dist/remote-hook.d.ts +12 -0
- package/dist/remote-hook.js +196 -0
- package/dist/replace-file.d.ts +50 -0
- package/dist/replace-file.js +328 -0
- package/dist/schedule-command.d.ts +35 -0
- package/dist/schedule-command.js +260 -0
- package/dist/schedule-launch.d.ts +15 -0
- package/dist/schedule-launch.js +24 -0
- package/dist/schedule-ready.d.ts +44 -0
- package/dist/schedule-ready.js +71 -0
- package/dist/schedule-remote.d.ts +77 -0
- package/dist/schedule-remote.js +167 -0
- package/dist/schedule-runner.d.ts +89 -0
- package/dist/schedule-runner.js +452 -0
- package/dist/schedule.d.ts +149 -0
- package/dist/schedule.js +428 -0
- package/dist/secret-mask.d.ts +29 -0
- package/dist/secret-mask.js +245 -0
- package/dist/self-register.d.ts +45 -0
- package/dist/self-register.js +67 -0
- package/dist/send.d.ts +56 -0
- package/dist/send.js +104 -0
- package/dist/session-diff.d.ts +161 -0
- package/dist/session-diff.js +445 -0
- package/dist/session-registry.d.ts +143 -0
- package/dist/session-registry.js +216 -0
- package/dist/sessions-report.d.ts +59 -0
- package/dist/sessions-report.js +99 -0
- package/dist/shell-audit.d.ts +34 -0
- package/dist/shell-audit.js +47 -0
- package/dist/subagent-transcripts.d.ts +167 -0
- package/dist/subagent-transcripts.js +437 -0
- package/dist/subscriber-trust.d.ts +128 -0
- package/dist/subscriber-trust.js +397 -0
- package/dist/templates.d.ts +105 -0
- package/dist/templates.js +871 -0
- package/dist/tmux-install.d.ts +44 -0
- package/dist/tmux-install.js +65 -0
- package/dist/tmux-liveness.d.ts +45 -0
- package/dist/tmux-liveness.js +71 -0
- package/dist/tmux-pane.d.ts +55 -0
- package/dist/tmux-pane.js +126 -0
- package/dist/tmux-socket.d.ts +20 -0
- package/dist/tmux-socket.js +292 -0
- package/dist/todo-fold.d.ts +31 -0
- package/dist/todo-fold.js +102 -0
- package/dist/transcript-tail.d.ts +312 -0
- package/dist/transcript-tail.js +718 -0
- package/dist/trust-command.d.ts +49 -0
- package/dist/trust-command.js +200 -0
- package/dist/trust-lock.d.ts +26 -0
- package/dist/trust-lock.js +94 -0
- package/dist/trust-pins.d.ts +37 -0
- package/dist/trust-pins.js +82 -0
- package/dist/turn-recorder-wire.d.ts +70 -0
- package/dist/turn-recorder-wire.js +165 -0
- package/dist/turn-recorder.d.ts +190 -0
- package/dist/turn-recorder.js +695 -0
- package/dist/turn-ring.d.ts +93 -0
- package/dist/turn-ring.js +418 -0
- package/dist/turn-watch.d.ts +177 -0
- package/dist/turn-watch.js +630 -0
- package/dist/types.d.ts +63 -0
- package/dist/types.js +2 -0
- package/dist/uninstall.d.ts +55 -0
- package/dist/uninstall.js +507 -0
- package/dist/usage.d.ts +17 -0
- package/dist/usage.js +421 -0
- package/dist/verify.d.ts +47 -0
- package/dist/verify.js +396 -0
- package/dist/worktree.d.ts +144 -0
- package/dist/worktree.js +310 -0
- package/dist/wrapper.d.ts +254 -0
- package/dist/wrapper.js +704 -0
- package/dist/ws-url.d.ts +109 -0
- package/dist/ws-url.js +202 -0
- package/package.json +78 -10
- package/bin/hwip.js +0 -3
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* `hwip schedule` — add, list and remove scheduled sessions (F8).
|
|
4
|
+
*
|
|
5
|
+
* This only writes and reads the files in `schedule.ts`. The listener starts the sessions
|
|
6
|
+
* (`schedule-runner.ts`), so nothing here needs a running listener except the sessions firing.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.handleSchedule = exports.liveScheduleDeps = exports.runSchedule = exports.buildSchedule = exports.describeWhen = void 0;
|
|
10
|
+
const args_js_1 = require("./args.js");
|
|
11
|
+
const schedule_js_1 = require("./schedule.js");
|
|
12
|
+
const config_js_1 = require("./config.js");
|
|
13
|
+
const listener_process_js_1 = require("./listener-process.js");
|
|
14
|
+
const remote_agents_js_1 = require("./remote-agents.js");
|
|
15
|
+
const schedule_ready_js_1 = require("./schedule-ready.js");
|
|
16
|
+
const wrapper_js_1 = require("./wrapper.js");
|
|
17
|
+
const worktree_js_1 = require("./worktree.js");
|
|
18
|
+
const USAGE = "usage: hwip schedule add <agent> (--at HH:MM [--days daily|weekdays|mon,wed,…] | --every <N>m|<N>h) [--prompt <text>] [--label <x>] [--wt <branch>] [--max-runtime <N>m|<N>h] [-- <agent args…>]\n"
|
|
19
|
+
+ ' hwip schedule [list] [--json]\n'
|
|
20
|
+
+ ' hwip schedule rm <id>';
|
|
21
|
+
const LISTENER_NOTE = "hwip: schedules start only while the listener runs ('hwip listener --install-service' keeps it running at login)";
|
|
22
|
+
/** A value flag's text: undefined when not given; an error when given without a value. */
|
|
23
|
+
const flagText = (flags, name) => {
|
|
24
|
+
const value = flags[name];
|
|
25
|
+
if (value === undefined)
|
|
26
|
+
return { ok: true, value: undefined };
|
|
27
|
+
if (typeof value !== 'string')
|
|
28
|
+
return { ok: false, error: `--${name} needs a value` };
|
|
29
|
+
return { ok: true, value };
|
|
30
|
+
};
|
|
31
|
+
/** `daily 09:00`, `weekdays 09:00`, `mon,wed 09:00`, `every 30m`, `every 2h`. */
|
|
32
|
+
const describeWhen = (when) => {
|
|
33
|
+
if (when.kind === 'every')
|
|
34
|
+
return when.minutes % 60 === 0 ? `every ${when.minutes / 60}h` : `every ${when.minutes}m`;
|
|
35
|
+
const days = when.days.join(',');
|
|
36
|
+
const label = days === schedule_js_1.ALL_DAYS.join(',') ? 'daily' : days === schedule_js_1.WEEKDAYS.join(',') ? 'weekdays' : when.days.map((d) => schedule_js_1.DAY_NAMES[d]).join(',');
|
|
37
|
+
return `${label} ${when.at}`;
|
|
38
|
+
};
|
|
39
|
+
exports.describeWhen = describeWhen;
|
|
40
|
+
/** The schedule `hwip schedule add <words>` asks for, before it has an id. */
|
|
41
|
+
const buildSchedule = async (words, deps) => {
|
|
42
|
+
const cut = words.indexOf('--');
|
|
43
|
+
const own = cut < 0 ? words : words.slice(0, cut);
|
|
44
|
+
const agentArgs = cut < 0 ? [] : words.slice(cut + 1);
|
|
45
|
+
// `parseArgs` reads the words after the program name and script; the first word is the agent.
|
|
46
|
+
const flags = (0, args_js_1.parseArgs)(['node', 'hwip', ...own]);
|
|
47
|
+
const agentWord = String(flags._command);
|
|
48
|
+
if (!agentWord)
|
|
49
|
+
return { ok: false, error: 'schedule add needs an agent (hwip cc, codex, …)' };
|
|
50
|
+
const agent = (0, remote_agents_js_1.findAgentBySubcommand)(agentWord);
|
|
51
|
+
if (!agent)
|
|
52
|
+
return { ok: false, error: `'${agentWord}' is not an agent hwip can start` };
|
|
53
|
+
if (flags._arg1)
|
|
54
|
+
return { ok: false, error: `unexpected word '${String(flags._arg1)}'` };
|
|
55
|
+
const read = (name) => flagText(flags, name);
|
|
56
|
+
const [at, days, every, prompt, label, wt, maxRuntime] = ['at', 'days', 'every', 'prompt', 'label', 'wt', 'max-runtime'].map(read);
|
|
57
|
+
const bad = [at, days, every, prompt, label, wt, maxRuntime].find((r) => !r.ok);
|
|
58
|
+
if (bad && !bad.ok)
|
|
59
|
+
return bad;
|
|
60
|
+
const value = (r) => (r.ok ? r.value : undefined);
|
|
61
|
+
const when = (0, schedule_js_1.parseWhen)({ at: value(at), days: value(days), every: value(every) });
|
|
62
|
+
if (!when.ok)
|
|
63
|
+
return when;
|
|
64
|
+
if (value(prompt) !== undefined && value(prompt).trim() === '')
|
|
65
|
+
return { ok: false, error: '--prompt must not be empty' };
|
|
66
|
+
if (value(prompt) !== undefined && !deps.promptKinds().includes(agent.kind)) {
|
|
67
|
+
const names = remote_agents_js_1.REMOTE_AGENTS.filter((a) => deps.promptKinds().includes(a.kind)).map((a) => a.subcommands[0]).join(', ');
|
|
68
|
+
return {
|
|
69
|
+
ok: false,
|
|
70
|
+
error: `${agentWord} cannot take a scheduled --prompt: hwip has no rule that tells when it is idle, and a prompt typed into a dialog would answer it. `
|
|
71
|
+
+ `--prompt works with: ${names || '(none right now)'}. A schedule without --prompt is fine.`,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
if (value(wt) !== undefined && value(label) !== undefined)
|
|
75
|
+
return { ok: false, error: '--wt and --label cannot be combined: a worktree session is named for its branch' };
|
|
76
|
+
let runtime = (0, schedule_js_1.defaultMaxRuntimeMin)(when.value);
|
|
77
|
+
if (value(maxRuntime) !== undefined) {
|
|
78
|
+
const parsed = (0, schedule_js_1.parseDuration)(value(maxRuntime), '--max-runtime', '90m or 2h');
|
|
79
|
+
if (!parsed.ok)
|
|
80
|
+
return parsed;
|
|
81
|
+
runtime = parsed.value;
|
|
82
|
+
}
|
|
83
|
+
const cwd = deps.projectDir();
|
|
84
|
+
let project = (0, wrapper_js_1.safeBasename)(cwd);
|
|
85
|
+
const branch = value(wt)?.trim();
|
|
86
|
+
if (branch !== undefined) {
|
|
87
|
+
const root = deps.checkoutRoot(cwd);
|
|
88
|
+
if (!root)
|
|
89
|
+
return { ok: false, error: '--wt needs a git checkout with a main worktree' };
|
|
90
|
+
if (!branch || branch.startsWith('-') || !(await deps.validBranch(root, branch))) {
|
|
91
|
+
return { ok: false, error: `'${branch}' is not a valid branch name` };
|
|
92
|
+
}
|
|
93
|
+
// The worktree directory `ensureWorktree` makes, and so the name the wrapper gives the session.
|
|
94
|
+
const dirName = (0, worktree_js_1.worktreeDirName)(root, branch);
|
|
95
|
+
if (!dirName)
|
|
96
|
+
return { ok: false, error: `'${branch}' is not a usable worktree name` };
|
|
97
|
+
project = dirName;
|
|
98
|
+
}
|
|
99
|
+
const createdAt = new Date(deps.now()).toISOString();
|
|
100
|
+
const text = value(prompt);
|
|
101
|
+
const make = (id) => {
|
|
102
|
+
const name = branch === undefined && value(label) !== undefined ? (0, wrapper_js_1.sanitizeLabel)(value(label)) : `sched-${id}`;
|
|
103
|
+
return {
|
|
104
|
+
v: 1,
|
|
105
|
+
id,
|
|
106
|
+
createdAt,
|
|
107
|
+
source: 'cli',
|
|
108
|
+
agentKind: agent.kind,
|
|
109
|
+
cwd,
|
|
110
|
+
label: name,
|
|
111
|
+
session: `${(0, wrapper_js_1.tmuxSessionName)(project)}-${name}`,
|
|
112
|
+
...(branch !== undefined ? { wt: branch } : {}),
|
|
113
|
+
...(agentArgs.length > 0 ? { agentArgs } : {}),
|
|
114
|
+
...(text !== undefined ? { prompt: text } : {}),
|
|
115
|
+
when: when.value,
|
|
116
|
+
maxRuntimeMin: runtime,
|
|
117
|
+
};
|
|
118
|
+
};
|
|
119
|
+
// The checks that need no id run now, so a refusal costs no id and no file.
|
|
120
|
+
const probe = (0, schedule_js_1.validateSchedule)(make('aaaaaa'));
|
|
121
|
+
return probe.ok ? { ok: true, value: { make, agentName: agent.displayName } } : probe;
|
|
122
|
+
};
|
|
123
|
+
exports.buildSchedule = buildSchedule;
|
|
124
|
+
/** An ISO date, or null for a time outside what a Date holds (a damaged or hand-edited file). */
|
|
125
|
+
const isoOrNull = (ms) => (ms !== null && Number.isFinite(new Date(ms).getTime()) ? new Date(ms).toISOString() : null);
|
|
126
|
+
const formatTime = (ms) => {
|
|
127
|
+
if (!Number.isFinite(new Date(ms).getTime()))
|
|
128
|
+
return '-';
|
|
129
|
+
const d = new Date(ms);
|
|
130
|
+
const pad = (n) => String(n).padStart(2, '0');
|
|
131
|
+
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
|
|
132
|
+
};
|
|
133
|
+
const handleAdd = async (words, json, deps) => {
|
|
134
|
+
const built = await (0, exports.buildSchedule)(words, deps);
|
|
135
|
+
if (!built.ok) {
|
|
136
|
+
deps.err(`hwip schedule: ${built.error}`);
|
|
137
|
+
return 2;
|
|
138
|
+
}
|
|
139
|
+
const added = (0, schedule_js_1.addSchedule)(built.value.make);
|
|
140
|
+
if (!added.ok) {
|
|
141
|
+
deps.err(`hwip schedule: ${added.error}`);
|
|
142
|
+
return 2;
|
|
143
|
+
}
|
|
144
|
+
const { schedule } = added;
|
|
145
|
+
const next = (0, schedule_js_1.nextOccurrence)(schedule.when, deps.now(), Date.parse(schedule.createdAt));
|
|
146
|
+
if (json) {
|
|
147
|
+
deps.out(JSON.stringify({ ...schedule, nextAt: isoOrNull(next) }));
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
deps.out(`scheduled ${schedule.id}: ${built.value.agentName} in ${(0, wrapper_js_1.safeBasename)(schedule.cwd)}, ${(0, exports.describeWhen)(schedule.when)}`
|
|
151
|
+
+ `${next === null ? '' : `, next ${formatTime(next)}`} (session ${schedule.session}, ends after ${schedule.maxRuntimeMin}m)`);
|
|
152
|
+
}
|
|
153
|
+
if (!deps.listenerRunning())
|
|
154
|
+
deps.err(LISTENER_NOTE);
|
|
155
|
+
return 0;
|
|
156
|
+
};
|
|
157
|
+
const handleList = (json, deps) => {
|
|
158
|
+
const { schedules, unreadable } = (0, schedule_js_1.listSchedules)();
|
|
159
|
+
const state = (0, schedule_js_1.readScheduleState)().schedules;
|
|
160
|
+
const now = deps.now();
|
|
161
|
+
const rows = schedules.map((schedule) => {
|
|
162
|
+
const next = (0, schedule_js_1.nextOccurrence)(schedule.when, now, Date.parse(schedule.createdAt));
|
|
163
|
+
const last = state[schedule.id]?.last;
|
|
164
|
+
return { schedule, next, last, running: state[schedule.id]?.running !== undefined };
|
|
165
|
+
});
|
|
166
|
+
if (json) {
|
|
167
|
+
deps.out(JSON.stringify(rows.map(({ schedule, next, last, running }) => ({
|
|
168
|
+
id: schedule.id,
|
|
169
|
+
agent: schedule.agentKind,
|
|
170
|
+
cwd: schedule.cwd,
|
|
171
|
+
label: schedule.label,
|
|
172
|
+
session: schedule.session,
|
|
173
|
+
when: schedule.when,
|
|
174
|
+
maxRuntimeMin: schedule.maxRuntimeMin,
|
|
175
|
+
source: schedule.source,
|
|
176
|
+
nextAt: isoOrNull(next),
|
|
177
|
+
last: last ? { outcome: last.outcome, at: isoOrNull(last.at) } : null,
|
|
178
|
+
running,
|
|
179
|
+
}))));
|
|
180
|
+
}
|
|
181
|
+
else if (rows.length === 0) {
|
|
182
|
+
deps.out("no schedules (add one with 'hwip schedule add')");
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
for (const { schedule, next, last, running } of rows) {
|
|
186
|
+
deps.out([
|
|
187
|
+
schedule.id,
|
|
188
|
+
schedule.agentKind,
|
|
189
|
+
(0, wrapper_js_1.safeBasename)(schedule.cwd),
|
|
190
|
+
(0, exports.describeWhen)(schedule.when),
|
|
191
|
+
`next ${next === null ? '-' : formatTime(next)}`,
|
|
192
|
+
`last ${last ? `${last.outcome} ${formatTime(last.at)}` : '-'}${running ? ' (running)' : ''}`,
|
|
193
|
+
].join(' '));
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
if (unreadable > 0)
|
|
197
|
+
deps.err(`hwip schedule: ${unreadable} file${unreadable === 1 ? '' : 's'} in ${(0, schedule_js_1.schedulesDir)()} could not be read and ${unreadable === 1 ? 'was' : 'were'} skipped`);
|
|
198
|
+
if (rows.length > 0 && !deps.listenerRunning())
|
|
199
|
+
deps.err(LISTENER_NOTE);
|
|
200
|
+
return 0;
|
|
201
|
+
};
|
|
202
|
+
const handleRemove = (id, deps) => {
|
|
203
|
+
if (!id) {
|
|
204
|
+
deps.err('hwip schedule: rm needs a schedule id');
|
|
205
|
+
return 2;
|
|
206
|
+
}
|
|
207
|
+
const removed = (0, schedule_js_1.removeSchedule)(id);
|
|
208
|
+
if (removed === 'invalid') {
|
|
209
|
+
deps.err(`hwip schedule: '${id}' is not a schedule id`);
|
|
210
|
+
return 2;
|
|
211
|
+
}
|
|
212
|
+
if (removed === 'missing') {
|
|
213
|
+
deps.err(`hwip schedule: no schedule ${id}`);
|
|
214
|
+
return 1;
|
|
215
|
+
}
|
|
216
|
+
deps.out(`removed ${id}. A session it already started keeps running; end it yourself if you want it gone.`);
|
|
217
|
+
return 0;
|
|
218
|
+
};
|
|
219
|
+
const JSON_ON = new Set(['--json', '--json=true', '--json=1', '--json=yes']);
|
|
220
|
+
/** `words`: argv after `schedule`. Returns the exit code. */
|
|
221
|
+
const runSchedule = async (words, deps) => {
|
|
222
|
+
// `--json` is lifted out first: the flag parser would hand it the next word (`--json list`, `add --json cc`).
|
|
223
|
+
const cut = words.indexOf('--');
|
|
224
|
+
const head = cut < 0 ? words : words.slice(0, cut);
|
|
225
|
+
const json = head.some((w) => JSON_ON.has(w));
|
|
226
|
+
const plain = [...head.filter((w) => !JSON_ON.has(w)), ...(cut < 0 ? [] : words.slice(cut))];
|
|
227
|
+
const sub = plain.length === 0 ? 'list' : plain[0];
|
|
228
|
+
const rest = plain.slice(1);
|
|
229
|
+
switch (sub) {
|
|
230
|
+
case 'add':
|
|
231
|
+
return handleAdd(rest, json, deps);
|
|
232
|
+
case 'list':
|
|
233
|
+
if (rest.length > 0)
|
|
234
|
+
break;
|
|
235
|
+
return handleList(json, deps);
|
|
236
|
+
case 'rm':
|
|
237
|
+
if (rest.length > 1)
|
|
238
|
+
break;
|
|
239
|
+
return handleRemove(rest[0], deps);
|
|
240
|
+
default:
|
|
241
|
+
}
|
|
242
|
+
deps.err(rest.length > 0 && (sub === 'list' || sub === 'rm')
|
|
243
|
+
? `hwip schedule: unexpected word '${rest[sub === 'list' ? 0 : 1]}'\n${USAGE}`
|
|
244
|
+
: `hwip schedule: unknown subcommand '${sub}'\n${USAGE}`);
|
|
245
|
+
return 2;
|
|
246
|
+
};
|
|
247
|
+
exports.runSchedule = runSchedule;
|
|
248
|
+
const liveScheduleDeps = () => ({
|
|
249
|
+
now: () => Date.now(),
|
|
250
|
+
projectDir: wrapper_js_1.detectProjectDir,
|
|
251
|
+
checkoutRoot: (dir) => (0, config_js_1.checkoutOf)(dir)?.root ?? null,
|
|
252
|
+
validBranch: (root, name) => (0, worktree_js_1.isValidBranchName)(root, name),
|
|
253
|
+
listenerRunning: () => (0, listener_process_js_1.runningListenerPid)() !== null,
|
|
254
|
+
promptKinds: () => (0, schedule_ready_js_1.promptableScheduleKinds)(remote_agents_js_1.REMOTE_AGENTS.map((a) => a.kind)),
|
|
255
|
+
out: (line) => console.log(line),
|
|
256
|
+
err: (line) => console.error(line),
|
|
257
|
+
});
|
|
258
|
+
exports.liveScheduleDeps = liveScheduleDeps;
|
|
259
|
+
const handleSchedule = (words) => (0, exports.runSchedule)(words, (0, exports.liveScheduleDeps)());
|
|
260
|
+
exports.handleSchedule = handleSchedule;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runs `hwip <agent> --detach …` for a scheduled session, as a child of the listener (F8). The child gets the schedule's
|
|
3
|
+
* directory as its cwd and none of the project-dir env vars, so it resolves its project from that directory and not from
|
|
4
|
+
* whatever shell once started the listener; and it is told it is the listener's own, so it does not "repair" the listener
|
|
5
|
+
* that is running it (see `LISTENER_LAUNCH_ENV`).
|
|
6
|
+
*/
|
|
7
|
+
import { execFile as nodeExecFile } from 'child_process';
|
|
8
|
+
import type { LaunchResult } from './schedule-runner.js';
|
|
9
|
+
export interface LaunchDeps {
|
|
10
|
+
execFile?: typeof nodeExecFile;
|
|
11
|
+
cliPath?: () => string | null;
|
|
12
|
+
env?: NodeJS.ProcessEnv;
|
|
13
|
+
}
|
|
14
|
+
export declare const launchHwip: (argv: string[], cwd: string, timeoutMs: number, deps?: LaunchDeps) => Promise<LaunchResult>;
|
|
15
|
+
//# sourceMappingURL=schedule-launch.d.ts.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Runs `hwip <agent> --detach …` for a scheduled session, as a child of the listener (F8). The child gets the schedule's
|
|
4
|
+
* directory as its cwd and none of the project-dir env vars, so it resolves its project from that directory and not from
|
|
5
|
+
* whatever shell once started the listener; and it is told it is the listener's own, so it does not "repair" the listener
|
|
6
|
+
* that is running it (see `LISTENER_LAUNCH_ENV`).
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.launchHwip = void 0;
|
|
10
|
+
const child_process_1 = require("child_process");
|
|
11
|
+
const config_js_1 = require("./config.js");
|
|
12
|
+
const listener_process_js_1 = require("./listener-process.js");
|
|
13
|
+
const launchHwip = (argv, cwd, timeoutMs, deps = {}) => new Promise((resolve) => {
|
|
14
|
+
const cliPath = (deps.cliPath ?? listener_process_js_1.resolveCliPath)();
|
|
15
|
+
if (!cliPath)
|
|
16
|
+
return resolve({ ok: false, error: 'cannot find the hwip cli entry' });
|
|
17
|
+
const env = { ...(deps.env ?? process.env), [listener_process_js_1.LISTENER_LAUNCH_ENV]: '1' };
|
|
18
|
+
for (const key of config_js_1.PROJECT_DIR_ENV_VARS)
|
|
19
|
+
delete env[key];
|
|
20
|
+
(deps.execFile ?? child_process_1.execFile)(process.execPath, [cliPath, ...argv], { cwd, env, encoding: 'utf-8', timeout: timeoutMs }, (err, _stdout, stderr) => {
|
|
21
|
+
resolve(err ? { ok: false, error: stderr || err.message } : { ok: true });
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
exports.launchHwip = launchHwip;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether a scheduled session's agent may be typed at (F8). A prompt typed into a terminal that is still starting up
|
|
3
|
+
* is lost or lands in the wrong place, and one typed into a dialog answers it: Enter on a trust or permission prompt
|
|
4
|
+
* accepts the default, with nobody there to read it. So the only sign this feature accepts is the agent's own state
|
|
5
|
+
* rules saying `idle` for the screen as it is now. An agent without an `idle` rule is never typed at — a screen that
|
|
6
|
+
* has stopped changing proves nothing, a static dialog stops changing too.
|
|
7
|
+
*
|
|
8
|
+
* The rules are the bundled ones, never the OTA manifest. A phone's input is typed by a person who has seen the
|
|
9
|
+
* screen; a schedule is typed by nobody. The OTA manifest is checked for its shape only (not signed) and replaces
|
|
10
|
+
* the bundle whole, so a rule set it carried could call an approval dialog `idle` and have the user's prompt plus
|
|
11
|
+
* Enter typed into it. Phone input and state reports keep using the OTA rules.
|
|
12
|
+
*
|
|
13
|
+
* One idle read is not enough either: a dialog can open after the composer is drawn (codex shows its start-up
|
|
14
|
+
* dialogs before the composer, but nothing promises every dialog does), and a read taken just before it opened says
|
|
15
|
+
* `idle`. So a session is ready only when two reads in a row — the last poll's and this one, taken right before the
|
|
16
|
+
* prompt is typed — show the same screen and the rules call it `idle`. Any change in between starts over. Digits are
|
|
17
|
+
* left out of the comparison, so a footer's clock, cost or token count does not keep a resting agent from ever being
|
|
18
|
+
* ready. The earlier read counts only while it is recent: a schedule's next run starts under the same session name,
|
|
19
|
+
* and its first read must not pair with the last screen of the run before.
|
|
20
|
+
*/
|
|
21
|
+
import { type DetectionManifest } from './agent-state.js';
|
|
22
|
+
export interface ReadyDeps {
|
|
23
|
+
/** The pane's foreground program is the agent of this kind (not the shell it started from). */
|
|
24
|
+
foregroundIsAgent: (session: string, agentKind: string) => boolean;
|
|
25
|
+
/** The pane's text as it is now, or null when it cannot be read. */
|
|
26
|
+
screenOf: (session: string) => string | null;
|
|
27
|
+
/** The detection rules' verdict on that text. */
|
|
28
|
+
stateOf: (screen: string, agentKind: string) => string;
|
|
29
|
+
now?: () => number;
|
|
30
|
+
}
|
|
31
|
+
/** How old the earlier of the two reads may be: two polls, so one slow tick still pairs and a past run never does. */
|
|
32
|
+
export declare const READ_PAIR_MAX_MS: number;
|
|
33
|
+
/** Whether the rules in `manifest` can ever say `idle` for `kind`: a rule that is on, and that decides the state. */
|
|
34
|
+
export declare const hasIdleRule: (manifest: DetectionManifest, kind: string) => boolean;
|
|
35
|
+
/** The kinds among `kinds` that a prompt can be scheduled for. */
|
|
36
|
+
export declare const promptableKinds: (manifest: DetectionManifest, kinds: readonly string[]) => string[];
|
|
37
|
+
export declare const createReadyCheck: (deps: ReadyDeps) => ((session: string, agentKind: string) => boolean);
|
|
38
|
+
/** Whether a schedule may carry a prompt for `kind`: the bundled rules have an `idle` rule for it. */
|
|
39
|
+
export declare const canPromptKind: (kind: string) => boolean;
|
|
40
|
+
/** `promptableKinds` over the bundled rules — what `schedule add --prompt` accepts. */
|
|
41
|
+
export declare const promptableScheduleKinds: (kinds: readonly string[]) => string[];
|
|
42
|
+
/** The state the bundled rules give a pane's text. */
|
|
43
|
+
export declare const scheduleScreenState: (content: string, kind: string) => string;
|
|
44
|
+
//# sourceMappingURL=schedule-ready.d.ts.map
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Whether a scheduled session's agent may be typed at (F8). A prompt typed into a terminal that is still starting up
|
|
4
|
+
* is lost or lands in the wrong place, and one typed into a dialog answers it: Enter on a trust or permission prompt
|
|
5
|
+
* accepts the default, with nobody there to read it. So the only sign this feature accepts is the agent's own state
|
|
6
|
+
* rules saying `idle` for the screen as it is now. An agent without an `idle` rule is never typed at — a screen that
|
|
7
|
+
* has stopped changing proves nothing, a static dialog stops changing too.
|
|
8
|
+
*
|
|
9
|
+
* The rules are the bundled ones, never the OTA manifest. A phone's input is typed by a person who has seen the
|
|
10
|
+
* screen; a schedule is typed by nobody. The OTA manifest is checked for its shape only (not signed) and replaces
|
|
11
|
+
* the bundle whole, so a rule set it carried could call an approval dialog `idle` and have the user's prompt plus
|
|
12
|
+
* Enter typed into it. Phone input and state reports keep using the OTA rules.
|
|
13
|
+
*
|
|
14
|
+
* One idle read is not enough either: a dialog can open after the composer is drawn (codex shows its start-up
|
|
15
|
+
* dialogs before the composer, but nothing promises every dialog does), and a read taken just before it opened says
|
|
16
|
+
* `idle`. So a session is ready only when two reads in a row — the last poll's and this one, taken right before the
|
|
17
|
+
* prompt is typed — show the same screen and the rules call it `idle`. Any change in between starts over. Digits are
|
|
18
|
+
* left out of the comparison, so a footer's clock, cost or token count does not keep a resting agent from ever being
|
|
19
|
+
* ready. The earlier read counts only while it is recent: a schedule's next run starts under the same session name,
|
|
20
|
+
* and its first read must not pair with the last screen of the run before.
|
|
21
|
+
*/
|
|
22
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
+
exports.scheduleScreenState = exports.promptableScheduleKinds = exports.canPromptKind = exports.createReadyCheck = exports.promptableKinds = exports.hasIdleRule = exports.READ_PAIR_MAX_MS = void 0;
|
|
24
|
+
const agent_rules_default_js_1 = require("./agent-rules.default.js");
|
|
25
|
+
const agent_state_js_1 = require("./agent-state.js");
|
|
26
|
+
const schedule_runner_js_1 = require("./schedule-runner.js");
|
|
27
|
+
/** How old the earlier of the two reads may be: two polls, so one slow tick still pairs and a past run never does. */
|
|
28
|
+
exports.READ_PAIR_MAX_MS = 2 * schedule_runner_js_1.PROMPT_POLL_MS;
|
|
29
|
+
/** A screen as the two reads compare it: digits do not count. */
|
|
30
|
+
const settledForm = (screen) => screen.replace(/\p{Nd}/gu, '0');
|
|
31
|
+
/** Whether the rules in `manifest` can ever say `idle` for `kind`: a rule that is on, and that decides the state. */
|
|
32
|
+
const hasIdleRule = (manifest, kind) => {
|
|
33
|
+
const disabled = new Set(manifest.disabledRuleIds ?? []);
|
|
34
|
+
return (manifest.agents[kind] ?? []).some((rule) => rule.state === 'idle' && !rule.skipStateUpdate && !disabled.has(rule.id));
|
|
35
|
+
};
|
|
36
|
+
exports.hasIdleRule = hasIdleRule;
|
|
37
|
+
/** The kinds among `kinds` that a prompt can be scheduled for. */
|
|
38
|
+
const promptableKinds = (manifest, kinds) => kinds.filter((kind) => (0, exports.hasIdleRule)(manifest, kind));
|
|
39
|
+
exports.promptableKinds = promptableKinds;
|
|
40
|
+
const createReadyCheck = (deps) => {
|
|
41
|
+
const now = deps.now ?? Date.now;
|
|
42
|
+
/** Per session, the idle screen the previous read saw and when. */
|
|
43
|
+
const lastIdle = new Map();
|
|
44
|
+
return (session, agentKind) => {
|
|
45
|
+
const screen = deps.foregroundIsAgent(session, agentKind) ? deps.screenOf(session) : null;
|
|
46
|
+
const previous = lastIdle.get(session);
|
|
47
|
+
if (screen === null || deps.stateOf(screen, agentKind) !== 'idle') {
|
|
48
|
+
lastIdle.delete(session);
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
const at = now();
|
|
52
|
+
const form = settledForm(screen);
|
|
53
|
+
if (previous?.screen !== form || at - previous.at > exports.READ_PAIR_MAX_MS) {
|
|
54
|
+
lastIdle.set(session, { screen: form, at });
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
// Spent: if the prompt then fails to go in, the next try reads twice again.
|
|
58
|
+
lastIdle.delete(session);
|
|
59
|
+
return true;
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
exports.createReadyCheck = createReadyCheck;
|
|
63
|
+
/** Whether a schedule may carry a prompt for `kind`: the bundled rules have an `idle` rule for it. */
|
|
64
|
+
const canPromptKind = (kind) => (0, exports.hasIdleRule)(agent_rules_default_js_1.DEFAULT_MANIFEST, kind);
|
|
65
|
+
exports.canPromptKind = canPromptKind;
|
|
66
|
+
/** `promptableKinds` over the bundled rules — what `schedule add --prompt` accepts. */
|
|
67
|
+
const promptableScheduleKinds = (kinds) => (0, exports.promptableKinds)(agent_rules_default_js_1.DEFAULT_MANIFEST, kinds);
|
|
68
|
+
exports.promptableScheduleKinds = promptableScheduleKinds;
|
|
69
|
+
/** The state the bundled rules give a pane's text. */
|
|
70
|
+
const scheduleScreenState = (content, kind) => (0, agent_state_js_1.evaluateState)(content, kind, agent_rules_default_js_1.DEFAULT_MANIFEST).state;
|
|
71
|
+
exports.scheduleScreenState = scheduleScreenState;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the phone sees and says about schedules (F8 slices 06 and 07). The decisions live here so
|
|
3
|
+
* `listener.ts` only wires them: this file reads the schedule files and the runner's state, and
|
|
4
|
+
* takes the phone's words at face value for nothing.
|
|
5
|
+
*/
|
|
6
|
+
import { type ScheduleOutcome, type ScheduleWhen } from './schedule.js';
|
|
7
|
+
import { type KnownSession } from './session-registry.js';
|
|
8
|
+
/** One schedule as the app lists it. No directory path and no agent arguments: the phone never names or reads either. */
|
|
9
|
+
export interface PhoneScheduleItem {
|
|
10
|
+
id: string;
|
|
11
|
+
source: 'cli' | 'phone';
|
|
12
|
+
agentKind: string;
|
|
13
|
+
/** The directory's name, not its path. */
|
|
14
|
+
project: string;
|
|
15
|
+
session: string;
|
|
16
|
+
when: ScheduleWhen;
|
|
17
|
+
prompt?: string;
|
|
18
|
+
maxRuntimeMin: number;
|
|
19
|
+
/** ISO time of the next run; null when there is none. */
|
|
20
|
+
nextAt: string | null;
|
|
21
|
+
last?: {
|
|
22
|
+
outcome: ScheduleOutcome;
|
|
23
|
+
at: string;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/** The longest prompt the list carries; the full text is in the file on this machine. */
|
|
27
|
+
export declare const LIST_PROMPT_CHARS = 500;
|
|
28
|
+
/** The sealed list must fit one relay frame (cf. `DIFF_SEALED_MAX_BYTES`), counted in UTF-8 bytes. */
|
|
29
|
+
export declare const LIST_MAX_BYTES: number;
|
|
30
|
+
/** What `phoneScheduleList` found: the schedules it could read, and how many files it could not. */
|
|
31
|
+
export interface ScheduleListing {
|
|
32
|
+
schedules: PhoneScheduleItem[];
|
|
33
|
+
/** Files that were there but unreadable or invalid (`hwip schedule` says so on stderr). */
|
|
34
|
+
unreadable: number;
|
|
35
|
+
}
|
|
36
|
+
/** The schedules on this machine for the app, oldest first, prompts cut to `promptChars`. Null when the directory could not be listed or holds more than a machine may: an empty list would then be a lie. */
|
|
37
|
+
export declare const phoneScheduleList: (now: number, promptChars?: number) => ScheduleListing | null;
|
|
38
|
+
/** The answer to a list request: what the app reads (the sealed text) and the list in it, or why there is none. */
|
|
39
|
+
export type ScheduleAnswer = {
|
|
40
|
+
ok: true;
|
|
41
|
+
listing: ScheduleListing;
|
|
42
|
+
sealed: string;
|
|
43
|
+
} | {
|
|
44
|
+
ok: false;
|
|
45
|
+
error: 'read_failed' | 'too_large';
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* The list as one answer. The text that gets sealed names the request and the time it answers, so a relay
|
|
49
|
+
* cannot hand an older sealed list back as the answer to a newer request. Prompts are cut shorter, step by
|
|
50
|
+
* step, until the answer fits a frame; it is an error only when even the list without prompts does not.
|
|
51
|
+
*/
|
|
52
|
+
export declare const scheduleAnswer: (now: number, requestId: string, at: string) => ScheduleAnswer;
|
|
53
|
+
/** The reasons a phone's schedule request is not carried out; they are `agent.command.refused` codes (`command-refusal.ts`). */
|
|
54
|
+
export type PhoneScheduleRefusal = 'schedule_invalid' | 'schedule_limit' | 'schedule_failed' | 'unknown_session';
|
|
55
|
+
export type PhoneScheduleResult = {
|
|
56
|
+
ok: true;
|
|
57
|
+
} | {
|
|
58
|
+
ok: false;
|
|
59
|
+
reason: PhoneScheduleRefusal;
|
|
60
|
+
why: string;
|
|
61
|
+
};
|
|
62
|
+
export interface PhoneScheduleDeps {
|
|
63
|
+
now: () => number;
|
|
64
|
+
/** The newest run of that tmux name (of that agent, when given) this machine recorded. */
|
|
65
|
+
recall: (session: string, agentKind?: string) => KnownSession | null;
|
|
66
|
+
directoryExists: (entry: KnownSession) => boolean;
|
|
67
|
+
/** Whether the agent can be typed a prompt by a schedule (`schedule-ready.ts`). */
|
|
68
|
+
canPrompt: (agentKind: string) => boolean;
|
|
69
|
+
}
|
|
70
|
+
export declare const livePhoneScheduleDeps: PhoneScheduleDeps;
|
|
71
|
+
/**
|
|
72
|
+
* Carry out one change the phone asked for: `{ op: 'create', when, prompt, agentKind?, maxRuntimeMin? }` for the
|
|
73
|
+
* recorded session `session`, or `{ op: 'remove', id }` for the schedule whose session that is. Nothing here
|
|
74
|
+
* types into a session. `request` is whatever the phone sealed, so every field is checked as it is used.
|
|
75
|
+
*/
|
|
76
|
+
export declare const applyPhoneSchedule: (request: unknown, session: string, deps?: PhoneScheduleDeps) => PhoneScheduleResult;
|
|
77
|
+
//# sourceMappingURL=schedule-remote.d.ts.map
|