@osolmaz/pi-workflows 0.16.0 → 0.16.1
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/README.md +18 -2
- package/dist/channels/adapter-entry.d.ts +3 -0
- package/dist/channels/adapter-entry.js +203 -0
- package/dist/channels/adapter-entry.js.map +1 -0
- package/dist/channels/config.d.ts +43 -0
- package/dist/channels/config.js +234 -0
- package/dist/channels/config.js.map +1 -0
- package/dist/channels/protocol.d.ts +94 -0
- package/dist/channels/protocol.js +219 -0
- package/dist/channels/protocol.js.map +1 -0
- package/dist/channels/telegram.d.ts +46 -0
- package/dist/channels/telegram.js +285 -0
- package/dist/channels/telegram.js.map +1 -0
- package/dist/client/client.d.ts +1 -0
- package/dist/client/client.js +5 -1
- package/dist/client/client.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/protocol.d.ts +1 -1
- package/dist/client/protocol.js +11 -5
- package/dist/client/protocol.js.map +1 -1
- package/dist/client/view.d.ts +42 -16
- package/dist/client/view.js.map +1 -1
- package/dist/controllers/index.d.ts +1 -1
- package/dist/controllers/index.js.map +1 -1
- package/dist/controllers/sqlite.d.ts +12 -165
- package/dist/controllers/sqlite.js +124 -435
- package/dist/controllers/sqlite.js.map +1 -1
- package/dist/extension/index.d.ts +23 -2
- package/dist/extension/index.js +619 -535
- package/dist/extension/index.js.map +1 -1
- package/dist/extension/recorder.d.ts +95 -0
- package/dist/extension/recorder.js +530 -0
- package/dist/extension/recorder.js.map +1 -0
- package/dist/extension/remote-recorder-store.d.ts +25 -0
- package/dist/extension/remote-recorder-store.js +81 -0
- package/dist/extension/remote-recorder-store.js.map +1 -0
- package/dist/extension/session-events.d.ts +134 -0
- package/dist/extension/session-events.js +60 -0
- package/dist/extension/session-events.js.map +1 -0
- package/dist/extension/session-view.d.ts +2 -0
- package/dist/extension/session-view.js +60 -2
- package/dist/extension/session-view.js.map +1 -1
- package/dist/extension/step-message.d.ts +6 -4
- package/dist/extension/step-message.js +12 -6
- package/dist/extension/step-message.js.map +1 -1
- package/dist/extension/widget.d.ts +2 -2
- package/dist/extension/widget.js +28 -18
- package/dist/extension/widget.js.map +1 -1
- package/dist/extension/workflow-message-coordinator.d.ts +27 -0
- package/dist/extension/workflow-message-coordinator.js +294 -0
- package/dist/extension/workflow-message-coordinator.js.map +1 -0
- package/dist/host/channel-effects.d.ts +72 -0
- package/dist/host/channel-effects.js +271 -0
- package/dist/host/channel-effects.js.map +1 -0
- package/dist/host/channel-supervisor.d.ts +21 -0
- package/dist/host/channel-supervisor.js +54 -0
- package/dist/host/channel-supervisor.js.map +1 -0
- package/dist/host/runner.d.ts +43 -8
- package/dist/host/runner.js +1567 -331
- package/dist/host/runner.js.map +1 -1
- package/dist/host/state.d.ts +19 -16
- package/dist/host/state.js +251 -63
- package/dist/host/state.js.map +1 -1
- package/dist/host/view.d.ts +19 -9
- package/dist/host/view.js +222 -118
- package/dist/host/view.js.map +1 -1
- package/dist/state/prune.js +3 -25
- package/dist/state/prune.js.map +1 -1
- package/dist/state/schema.js +76 -97
- package/dist/state/schema.js.map +1 -1
- package/dist/state/workflow-messages.d.ts +96 -0
- package/dist/state/workflow-messages.js +335 -0
- package/dist/state/workflow-messages.js.map +1 -0
- package/dist/viewer/render.js +1 -1
- package/dist/viewer/render.js.map +1 -1
- package/dist/viewer/tui.d.ts +1 -1
- package/dist/viewer/tui.js +65 -8
- package/dist/viewer/tui.js.map +1 -1
- package/dist/workflows/human-decision.d.ts +2 -0
- package/dist/workflows/human-decision.js +8 -2
- package/dist/workflows/human-decision.js.map +1 -1
- package/dist/workflows/index.d.ts +1 -1
- package/dist/workflows/index.js.map +1 -1
- package/dist/workflows/settings.d.ts +1 -8
- package/dist/workflows/settings.js.map +1 -1
- package/dist/workflows/store.d.ts +5 -19
- package/dist/workflows/store.js +121 -354
- package/dist/workflows/store.js.map +1 -1
- package/dist/workflows/tool-input.d.ts +21 -0
- package/dist/workflows/tool-input.js +23 -1
- package/dist/workflows/tool-input.js.map +1 -1
- package/dist/workflows/types.d.ts +5 -5
- package/dist/workflows/workflow-message-content.d.ts +38 -0
- package/dist/workflows/workflow-message-content.js +157 -0
- package/dist/workflows/workflow-message-content.js.map +1 -0
- package/docs/2026-08-18-herdr-piw-plan.md +2 -1
- package/docs/2026-08-20-durable-workflow-launch-plan.md +1 -1
- package/docs/2026-08-25-workflow-follow-ups.md +26 -50
- package/docs/2026-08-30-out-of-process-workflow-host-plan.md +3 -3
- package/docs/2026-09-01-restore-session-delivery-controls-plan.md +4 -0
- package/docs/2026-09-01-unified-workflow-client-plan.md +5 -1
- package/docs/2026-09-02-installed-live-e2e-plan.md +15 -1
- package/docs/2026-09-02-unify-workflow-messages-plan.md +562 -0
- package/docs/DEFERRED_TURNS.md +66 -294
- package/docs/HUMAN_DECISIONS.md +29 -30
- package/docs/HUMAN_DECISION_PRESENTATIONS.md +4 -11
- package/docs/SQLITE_STATE.md +19 -15
- package/docs/WORKFLOW_HOST.md +93 -28
- package/docs/WORKFLOW_STEP_MESSAGES.md +166 -75
- package/docs/live-replay-protocol.md +8 -4
- package/docs/tui-viewer.md +3 -1
- package/docs/workflows.md +34 -27
- package/herdr-plugin.toml +1 -1
- package/package.json +1 -1
- package/plugins/herdr/viewer.mjs +14 -5
- package/src/channels/adapter-entry.ts +220 -0
- package/src/channels/config.ts +296 -0
- package/src/channels/protocol.ts +333 -0
- package/src/channels/telegram.ts +335 -0
- package/src/client/client.ts +6 -2
- package/src/client/index.ts +2 -1
- package/src/client/protocol.ts +11 -5
- package/src/client/view.ts +43 -18
- package/src/controllers/index.ts +0 -1
- package/src/controllers/sqlite.ts +164 -844
- package/src/extension/index.ts +693 -646
- package/src/extension/recorder.ts +687 -0
- package/src/extension/remote-recorder-store.ts +126 -0
- package/src/extension/session-events.ts +119 -0
- package/src/extension/session-view.ts +86 -3
- package/src/extension/step-message.ts +19 -9
- package/src/extension/widget.ts +37 -17
- package/src/extension/workflow-message-coordinator.ts +334 -0
- package/src/host/channel-effects.ts +465 -0
- package/src/host/channel-supervisor.ts +73 -0
- package/src/host/runner.ts +2120 -531
- package/src/host/state.ts +333 -94
- package/src/host/view.ts +249 -121
- package/src/state/prune.ts +3 -31
- package/src/state/schema.ts +76 -97
- package/src/state/workflow-messages.ts +548 -0
- package/src/viewer/render.ts +1 -5
- package/src/viewer/tui.ts +71 -6
- package/src/workflows/human-decision.ts +18 -2
- package/src/workflows/index.ts +0 -1
- package/src/workflows/settings.ts +1 -20
- package/src/workflows/store.ts +173 -489
- package/src/workflows/tool-input.ts +36 -1
- package/src/workflows/types.ts +5 -5
- package/src/workflows/workflow-message-content.ts +197 -0
- package/dist/extension/decision-channels.d.ts +0 -134
- package/dist/extension/decision-channels.js +0 -1307
- package/dist/extension/decision-channels.js.map +0 -1
- package/src/extension/decision-channels.ts +0 -1826
package/README.md
CHANGED
|
@@ -85,6 +85,12 @@ package-owned host is the only production process that opens the active
|
|
|
85
85
|
database. The extension, CLI, Herdr adapter, and `piw` use the same versioned
|
|
86
86
|
local client protocol. See [SQLite state](docs/SQLITE_STATE.md).
|
|
87
87
|
|
|
88
|
+
One host-owned workflow-message path handles steps, reminders, resumed work,
|
|
89
|
+
human decisions, notifications, terminal results, and follow-up prompts. The
|
|
90
|
+
shared extension coordinator reports exact Pi branches and model turns, so an
|
|
91
|
+
active workflow turn stays `running`, repeated polling cannot send the same
|
|
92
|
+
message twice, and a terminal result remains visible for 60 seconds.
|
|
93
|
+
|
|
88
94
|
## Quick start
|
|
89
95
|
|
|
90
96
|
Put a workflow file in `.pi/workflows/` (project) or `~/.pi/agent/workflows/`
|
|
@@ -128,6 +134,15 @@ A workflow can also expose [settings that change during a
|
|
|
128
134
|
run](docs/2026-08-25-workflow-settings.md) and queue [normal follow-up work
|
|
129
135
|
after completion](docs/2026-08-25-workflow-follow-ups.md).
|
|
130
136
|
|
|
137
|
+
Protected human decisions always appear in Pi. To add a private Telegram
|
|
138
|
+
channel, run `/workflow-channel setup` in Pi. Use `/workflow-channel status`
|
|
139
|
+
to inspect configured profiles and uncertain operations, and
|
|
140
|
+
`/workflow-channel reload` after a private configuration change. If an
|
|
141
|
+
external send has an uncertain result, check Telegram first, then use
|
|
142
|
+
`/workflow-channel recover <message-id> confirm|retry`. A retry can duplicate
|
|
143
|
+
a message when Telegram cannot prove the first result. See
|
|
144
|
+
[Human decisions](docs/HUMAN_DECISIONS.md).
|
|
145
|
+
|
|
131
146
|
Use `expectedOutput: assistantMessage()` when a normal assistant response must
|
|
132
147
|
be a node inside the graph rather than a presentation after the run. Its exact
|
|
133
148
|
visible text becomes the node output after the turn settles. The helper has no
|
|
@@ -218,8 +233,9 @@ earlier `autodevise` name, and the old command and export are not retained.
|
|
|
218
233
|
|
|
219
234
|
## Viewers
|
|
220
235
|
|
|
221
|
-
Runs persist in `~/.pi/agent/workflows/state.sqlite` as they execute. The
|
|
222
|
-
|
|
236
|
+
Runs persist in `~/.pi/agent/workflows/state.sqlite` as they execute. The global
|
|
237
|
+
host is the only process that opens this database. Viewers use the shared client
|
|
238
|
+
protocol and re-render from the host's run view on every state change:
|
|
223
239
|
|
|
224
240
|
```bash
|
|
225
241
|
pi-workflows view # interactive picker, live updates
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { createInterface } from "node:readline";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { canonicalJson } from "../state/json.js";
|
|
4
|
+
import { errorMessage } from "../workflows/errors.js";
|
|
5
|
+
import { CHANNEL_ADAPTER_PROTOCOL_SCHEMA, channelStableMessageId, encodeChannelLine, parseChannelAdapterLaunch, parseChannelAdapterResponse, } from "./protocol.js";
|
|
6
|
+
import { TelegramAdapter } from "./telegram.js";
|
|
7
|
+
class AdapterHostConnection {
|
|
8
|
+
launch;
|
|
9
|
+
sequence = 0;
|
|
10
|
+
revision = 0;
|
|
11
|
+
lines = createInterface({ input: process.stdin, crlfDelay: Infinity })[Symbol.asyncIterator]();
|
|
12
|
+
constructor(launch) {
|
|
13
|
+
this.launch = launch;
|
|
14
|
+
}
|
|
15
|
+
async report(message) {
|
|
16
|
+
this.sequence += 1;
|
|
17
|
+
const full = {
|
|
18
|
+
schema: CHANNEL_ADAPTER_PROTOCOL_SCHEMA,
|
|
19
|
+
adapterEpoch: this.launch.adapterEpoch,
|
|
20
|
+
profile: this.launch.profile,
|
|
21
|
+
sequence: this.sequence,
|
|
22
|
+
expectedRevision: this.revision,
|
|
23
|
+
...message,
|
|
24
|
+
};
|
|
25
|
+
if (!process.stdout.write(encodeChannelLine(full))) {
|
|
26
|
+
await new Promise((resolve) => process.stdout.once("drain", resolve));
|
|
27
|
+
}
|
|
28
|
+
const next = await this.lines.next();
|
|
29
|
+
if (next.done)
|
|
30
|
+
throw new Error("Workflow host closed the channel adapter connection");
|
|
31
|
+
const response = parseChannelAdapterResponse(Buffer.from(next.value, "utf8"));
|
|
32
|
+
if (response.sequence !== this.sequence) {
|
|
33
|
+
throw new Error("Workflow host returned a mismatched channel response");
|
|
34
|
+
}
|
|
35
|
+
if (response.outcome !== "accepted") {
|
|
36
|
+
throw new Error(response.error ?? "Workflow host rejected the channel adapter message");
|
|
37
|
+
}
|
|
38
|
+
this.revision = response.revision;
|
|
39
|
+
return response;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
export async function runChannelAdapter() {
|
|
43
|
+
const launch = readLaunch();
|
|
44
|
+
const host = new AdapterHostConnection(launch);
|
|
45
|
+
const telegram = new TelegramAdapter({
|
|
46
|
+
profile: launch.profile,
|
|
47
|
+
token: launch.token,
|
|
48
|
+
allowedUserIds: launch.allowedUserIds,
|
|
49
|
+
allowedChatIds: launch.allowedChatIds,
|
|
50
|
+
...(launch.apiBase === undefined ? {} : { apiBase: launch.apiBase }),
|
|
51
|
+
});
|
|
52
|
+
let cursor = 0;
|
|
53
|
+
let controlSequence = 0;
|
|
54
|
+
const controlId = (kind) => {
|
|
55
|
+
controlSequence += 1;
|
|
56
|
+
return channelStableMessageId([
|
|
57
|
+
launch.adapterEpoch,
|
|
58
|
+
kind,
|
|
59
|
+
String(controlSequence),
|
|
60
|
+
String(cursor),
|
|
61
|
+
]);
|
|
62
|
+
};
|
|
63
|
+
let stopping = false;
|
|
64
|
+
process.once("SIGTERM", () => {
|
|
65
|
+
stopping = true;
|
|
66
|
+
});
|
|
67
|
+
process.once("SIGINT", () => {
|
|
68
|
+
stopping = true;
|
|
69
|
+
});
|
|
70
|
+
let response = await host.report({
|
|
71
|
+
kind: "channel.ready",
|
|
72
|
+
stableMessageId: controlId("ready"),
|
|
73
|
+
cursor,
|
|
74
|
+
});
|
|
75
|
+
while (!stopping) {
|
|
76
|
+
const command = response.command;
|
|
77
|
+
if (command === null) {
|
|
78
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
79
|
+
response = await host.report({
|
|
80
|
+
kind: "channel.ready",
|
|
81
|
+
stableMessageId: controlId("ready"),
|
|
82
|
+
cursor,
|
|
83
|
+
});
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (command.kind === "channel.stop") {
|
|
87
|
+
stopping = true;
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
if (command.kind === "channel.present") {
|
|
91
|
+
try {
|
|
92
|
+
const messages = await telegram.present(command.request);
|
|
93
|
+
response = await host.report({
|
|
94
|
+
kind: "channel.present",
|
|
95
|
+
stableMessageId: command.stableMessageId,
|
|
96
|
+
decisionId: command.request.decisionId,
|
|
97
|
+
requestDigest: command.request.requestDigest,
|
|
98
|
+
attemptId: command.attemptId,
|
|
99
|
+
state: "confirmed",
|
|
100
|
+
messages,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
response = await host.report({
|
|
105
|
+
kind: "channel.present",
|
|
106
|
+
stableMessageId: command.stableMessageId,
|
|
107
|
+
decisionId: command.request.decisionId,
|
|
108
|
+
requestDigest: command.request.requestDigest,
|
|
109
|
+
attemptId: command.attemptId,
|
|
110
|
+
state: "unknown",
|
|
111
|
+
messages: [],
|
|
112
|
+
errorCode: errorMessage(error),
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
if (command.kind === "channel.settle") {
|
|
118
|
+
try {
|
|
119
|
+
await telegram.settle(command.outcome, command.response, command.messages);
|
|
120
|
+
response = await host.report({
|
|
121
|
+
kind: "channel.settle",
|
|
122
|
+
stableMessageId: command.stableMessageId,
|
|
123
|
+
decisionId: command.request.decisionId,
|
|
124
|
+
requestDigest: command.request.requestDigest,
|
|
125
|
+
attemptId: command.attemptId,
|
|
126
|
+
state: "confirmed",
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
response = await host.report({
|
|
131
|
+
kind: "channel.settle",
|
|
132
|
+
stableMessageId: command.stableMessageId,
|
|
133
|
+
decisionId: command.request.decisionId,
|
|
134
|
+
requestDigest: command.request.requestDigest,
|
|
135
|
+
attemptId: command.attemptId,
|
|
136
|
+
state: "unknown",
|
|
137
|
+
errorCode: errorMessage(error),
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
telegram.setRequests(command.requests);
|
|
143
|
+
const polled = await telegram.poll(command.cursor);
|
|
144
|
+
cursor = polled.cursor;
|
|
145
|
+
for (const answer of polled.answers) {
|
|
146
|
+
await host.report({
|
|
147
|
+
kind: "channel.answer",
|
|
148
|
+
stableMessageId: channelStableMessageId([
|
|
149
|
+
launch.profile,
|
|
150
|
+
answer.request.decisionId,
|
|
151
|
+
answer.eventId,
|
|
152
|
+
]),
|
|
153
|
+
decisionId: answer.request.decisionId,
|
|
154
|
+
requestDigest: answer.request.requestDigest,
|
|
155
|
+
response: answer.response,
|
|
156
|
+
actorId: answer.actorId,
|
|
157
|
+
chatId: answer.chatId,
|
|
158
|
+
eventId: answer.eventId,
|
|
159
|
+
idempotencyKey: `telegram:${launch.profile}:${answer.eventId}`,
|
|
160
|
+
cursor,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
response = await host.report({
|
|
164
|
+
kind: "channel.ready",
|
|
165
|
+
stableMessageId: controlId("ready"),
|
|
166
|
+
cursor,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
await host.report({
|
|
170
|
+
kind: "channel.exiting",
|
|
171
|
+
stableMessageId: controlId("exiting"),
|
|
172
|
+
cursor,
|
|
173
|
+
});
|
|
174
|
+
return 0;
|
|
175
|
+
}
|
|
176
|
+
function readLaunch() {
|
|
177
|
+
const encoded = process.env.PI_WORKFLOWS_CHANNEL_LAUNCH;
|
|
178
|
+
if (encoded === undefined)
|
|
179
|
+
throw new Error("Channel adapter launch envelope is missing");
|
|
180
|
+
let value;
|
|
181
|
+
try {
|
|
182
|
+
value = JSON.parse(Buffer.from(encoded, "base64url").toString("utf8"));
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
throw new Error("Channel adapter launch envelope is invalid");
|
|
186
|
+
}
|
|
187
|
+
return parseChannelAdapterLaunch(value);
|
|
188
|
+
}
|
|
189
|
+
async function main() {
|
|
190
|
+
try {
|
|
191
|
+
process.exitCode = await runChannelAdapter();
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
process.stderr.write(`${errorMessage(error)}\n`);
|
|
195
|
+
process.exitCode = 1;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
if (process.argv[1] === fileURLToPath(import.meta.url))
|
|
199
|
+
void main();
|
|
200
|
+
export function encodeChannelLaunch(launch) {
|
|
201
|
+
return Buffer.from(canonicalJson(launch)).toString("base64url");
|
|
202
|
+
}
|
|
203
|
+
//# sourceMappingURL=adapter-entry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter-entry.js","sourceRoot":"","sources":["../../src/channels/adapter-entry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export type DecisionChannelConfig = {
|
|
2
|
+
schema: "pi-workflows.channels.v1";
|
|
3
|
+
audiences: Record<string, {
|
|
4
|
+
channels: string[];
|
|
5
|
+
accept: "first-valid-answer";
|
|
6
|
+
}>;
|
|
7
|
+
telegramProfiles?: Record<string, {
|
|
8
|
+
credential: string;
|
|
9
|
+
allowedUserIds: string[];
|
|
10
|
+
allowedChatIds: string[];
|
|
11
|
+
}>;
|
|
12
|
+
};
|
|
13
|
+
export type DecisionCredentialConfig = {
|
|
14
|
+
schema: "pi-workflows.credentials.v1";
|
|
15
|
+
telegram: Record<string, {
|
|
16
|
+
tokenFile: string;
|
|
17
|
+
}>;
|
|
18
|
+
};
|
|
19
|
+
export type LoadedDecisionChannelConfig = {
|
|
20
|
+
channels: DecisionChannelConfig;
|
|
21
|
+
credentials: Record<string, string>;
|
|
22
|
+
configDir: string;
|
|
23
|
+
};
|
|
24
|
+
export type TelegramFetch = (input: string, init?: RequestInit) => Promise<{
|
|
25
|
+
ok: boolean;
|
|
26
|
+
status: number;
|
|
27
|
+
json(): Promise<unknown>;
|
|
28
|
+
}>;
|
|
29
|
+
export declare function decisionConfigDir(env?: NodeJS.ProcessEnv): string;
|
|
30
|
+
export declare function audienceChannels(config: DecisionChannelConfig | null, audience: string): string[];
|
|
31
|
+
export declare function loadDecisionChannelConfig(configDir?: string): Promise<LoadedDecisionChannelConfig | null>;
|
|
32
|
+
export declare function verifyTelegramTokenFile(tokenFile: string, fetchFn?: TelegramFetch, apiBase?: string): Promise<void>;
|
|
33
|
+
export declare function writeDecisionChannelProfile(options: {
|
|
34
|
+
configDir?: string;
|
|
35
|
+
audience: string;
|
|
36
|
+
profile: string;
|
|
37
|
+
credential: string;
|
|
38
|
+
tokenFile: string;
|
|
39
|
+
allowedUserIds: string[];
|
|
40
|
+
allowedChatIds: string[];
|
|
41
|
+
}): Promise<void>;
|
|
42
|
+
export declare function parseChannelConfig(value: unknown): DecisionChannelConfig;
|
|
43
|
+
export declare function parseCredentialConfig(value: unknown): DecisionCredentialConfig;
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
const DEFAULT_API_BASE = "https://api.telegram.org";
|
|
6
|
+
const NUMERIC_ID = /^-?[0-9]+$/;
|
|
7
|
+
const SIMPLE_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,199}$/;
|
|
8
|
+
export function decisionConfigDir(env = process.env) {
|
|
9
|
+
return env.PI_WORKFLOWS_CONFIG_DIR ?? path.join(os.homedir(), ".config", "pi-workflows");
|
|
10
|
+
}
|
|
11
|
+
export function audienceChannels(config, audience) {
|
|
12
|
+
return [...(config?.audiences[audience]?.channels ?? ["pi"])];
|
|
13
|
+
}
|
|
14
|
+
export async function loadDecisionChannelConfig(configDir = decisionConfigDir()) {
|
|
15
|
+
const channelPath = path.join(configDir, "channels.json");
|
|
16
|
+
let channelsRaw;
|
|
17
|
+
try {
|
|
18
|
+
channelsRaw = JSON.parse(await fs.readFile(channelPath, "utf8"));
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
if (error.code === "ENOENT")
|
|
22
|
+
return null;
|
|
23
|
+
throw error;
|
|
24
|
+
}
|
|
25
|
+
await requirePrivateFile(channelPath, "pi-workflows channel profile");
|
|
26
|
+
const channels = parseChannelConfig(channelsRaw);
|
|
27
|
+
const profiles = Object.values(channels.telegramProfiles ?? {});
|
|
28
|
+
if (profiles.length === 0)
|
|
29
|
+
return { channels, credentials: {}, configDir };
|
|
30
|
+
const credentialPath = path.join(configDir, "credentials.json");
|
|
31
|
+
await requirePrivateFile(credentialPath, "pi-workflows credential profile");
|
|
32
|
+
const credentialsConfig = parseCredentialConfig(JSON.parse(await fs.readFile(credentialPath, "utf8")));
|
|
33
|
+
const credentials = {};
|
|
34
|
+
for (const profile of profiles) {
|
|
35
|
+
const credential = credentialsConfig.telegram[profile.credential];
|
|
36
|
+
if (credential === undefined) {
|
|
37
|
+
throw new Error(`Telegram credential ${profile.credential} is not configured`);
|
|
38
|
+
}
|
|
39
|
+
if (!path.isAbsolute(credential.tokenFile)) {
|
|
40
|
+
throw new Error(`Telegram credential ${profile.credential} tokenFile must be absolute`);
|
|
41
|
+
}
|
|
42
|
+
await requirePrivateFile(credential.tokenFile, `Telegram credential ${profile.credential} token file`);
|
|
43
|
+
const token = (await fs.readFile(credential.tokenFile, "utf8")).trim();
|
|
44
|
+
if (token.length === 0) {
|
|
45
|
+
throw new Error(`Telegram credential ${profile.credential} token file is empty`);
|
|
46
|
+
}
|
|
47
|
+
credentials[profile.credential] = token;
|
|
48
|
+
}
|
|
49
|
+
return { channels, credentials, configDir };
|
|
50
|
+
}
|
|
51
|
+
export async function verifyTelegramTokenFile(tokenFile, fetchFn, apiBase = DEFAULT_API_BASE) {
|
|
52
|
+
const request = fetchFn ?? fetch;
|
|
53
|
+
if (!path.isAbsolute(tokenFile))
|
|
54
|
+
throw new Error("Telegram token file path must be absolute");
|
|
55
|
+
await requirePrivateFile(tokenFile, "Telegram token file");
|
|
56
|
+
const token = (await fs.readFile(tokenFile, "utf8")).trim();
|
|
57
|
+
if (token.length === 0)
|
|
58
|
+
throw new Error("Telegram token file is empty");
|
|
59
|
+
let response;
|
|
60
|
+
try {
|
|
61
|
+
response = await request(`${apiBase}/bot${encodeURIComponent(token)}/getMe`, {
|
|
62
|
+
method: "POST",
|
|
63
|
+
headers: { "content-type": "application/json" },
|
|
64
|
+
body: "{}",
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
throw new Error("Telegram credential verification did not return a response");
|
|
69
|
+
}
|
|
70
|
+
if (!response.ok) {
|
|
71
|
+
throw new Error(`Telegram credential verification failed with HTTP ${response.status}`);
|
|
72
|
+
}
|
|
73
|
+
const body = requireRecord(await response.json(), "Telegram credential verification response");
|
|
74
|
+
if (body.ok !== true)
|
|
75
|
+
throw new Error("Telegram credential verification was rejected");
|
|
76
|
+
}
|
|
77
|
+
export async function writeDecisionChannelProfile(options) {
|
|
78
|
+
const configDir = options.configDir ?? decisionConfigDir();
|
|
79
|
+
requireSimpleId(options.audience, "Decision audience");
|
|
80
|
+
requireSimpleId(options.profile, "Telegram profile");
|
|
81
|
+
requireSimpleId(options.credential, "Telegram credential");
|
|
82
|
+
validateNumericIds(options.allowedUserIds, "Telegram allowed user IDs");
|
|
83
|
+
validateNumericIds(options.allowedChatIds, "Telegram allowed chat IDs");
|
|
84
|
+
if (!path.isAbsolute(options.tokenFile)) {
|
|
85
|
+
throw new Error("Telegram token file path must be absolute");
|
|
86
|
+
}
|
|
87
|
+
await requirePrivateFile(options.tokenFile, "Telegram token file");
|
|
88
|
+
await fs.mkdir(configDir, { recursive: true, mode: 0o700 });
|
|
89
|
+
const channelPath = path.join(configDir, "channels.json");
|
|
90
|
+
const credentialPath = path.join(configDir, "credentials.json");
|
|
91
|
+
const existingChannels = await readOptionalJson(channelPath);
|
|
92
|
+
const existingCredentials = await readOptionalJson(credentialPath);
|
|
93
|
+
const channels = existingChannels === null
|
|
94
|
+
? { schema: "pi-workflows.channels.v1", audiences: {} }
|
|
95
|
+
: parseChannelConfig(existingChannels);
|
|
96
|
+
const credentials = existingCredentials === null
|
|
97
|
+
? { schema: "pi-workflows.credentials.v1", telegram: {} }
|
|
98
|
+
: parseCredentialConfig(existingCredentials);
|
|
99
|
+
channels.telegramProfiles = {
|
|
100
|
+
...channels.telegramProfiles,
|
|
101
|
+
[options.profile]: {
|
|
102
|
+
credential: options.credential,
|
|
103
|
+
allowedUserIds: [...options.allowedUserIds],
|
|
104
|
+
allowedChatIds: [...options.allowedChatIds],
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
const priorAudience = channels.audiences[options.audience];
|
|
108
|
+
const channelId = `telegram:${options.profile}`;
|
|
109
|
+
channels.audiences[options.audience] = {
|
|
110
|
+
channels: [...new Set([...(priorAudience?.channels ?? ["pi"]), channelId])],
|
|
111
|
+
accept: "first-valid-answer",
|
|
112
|
+
};
|
|
113
|
+
credentials.telegram[options.credential] = { tokenFile: options.tokenFile };
|
|
114
|
+
await writePrivateJson(channelPath, channels);
|
|
115
|
+
await writePrivateJson(credentialPath, credentials);
|
|
116
|
+
}
|
|
117
|
+
export function parseChannelConfig(value) {
|
|
118
|
+
const input = requireRecord(value, "Decision channel configuration");
|
|
119
|
+
if (input.schema !== "pi-workflows.channels.v1") {
|
|
120
|
+
throw new Error("Decision channel configuration schema is invalid");
|
|
121
|
+
}
|
|
122
|
+
const audiencesInput = requireRecord(input.audiences, "Decision channel audiences");
|
|
123
|
+
const audiences = {};
|
|
124
|
+
for (const [audience, raw] of Object.entries(audiencesInput)) {
|
|
125
|
+
requireSimpleId(audience, "Decision audience");
|
|
126
|
+
const record = requireRecord(raw, `Decision audience ${audience}`);
|
|
127
|
+
const channels = stringArray(record.channels, `Decision audience ${audience} channels`);
|
|
128
|
+
if (channels.length === 0)
|
|
129
|
+
throw new Error(`Decision audience ${audience} has no channels`);
|
|
130
|
+
if (record.accept !== "first-valid-answer") {
|
|
131
|
+
throw new Error(`Decision audience ${audience} accept policy is invalid`);
|
|
132
|
+
}
|
|
133
|
+
audiences[audience] = { channels, accept: "first-valid-answer" };
|
|
134
|
+
}
|
|
135
|
+
const telegramProfiles = {};
|
|
136
|
+
if (input.telegramProfiles !== undefined) {
|
|
137
|
+
const profiles = requireRecord(input.telegramProfiles, "Telegram profiles");
|
|
138
|
+
for (const [name, raw] of Object.entries(profiles)) {
|
|
139
|
+
requireSimpleId(name, "Telegram profile");
|
|
140
|
+
const profile = requireRecord(raw, `Telegram profile ${name}`);
|
|
141
|
+
const credential = requireSimpleId(profile.credential, `Telegram profile ${name} credential`);
|
|
142
|
+
const allowedUserIds = stringArray(profile.allowedUserIds, `Telegram profile ${name} allowedUserIds`);
|
|
143
|
+
const allowedChatIds = stringArray(profile.allowedChatIds, `Telegram profile ${name} allowedChatIds`);
|
|
144
|
+
validateNumericIds(allowedUserIds, `Telegram profile ${name} allowedUserIds`);
|
|
145
|
+
validateNumericIds(allowedChatIds, `Telegram profile ${name} allowedChatIds`);
|
|
146
|
+
if (allowedUserIds.length === 0 || allowedChatIds.length === 0) {
|
|
147
|
+
throw new Error(`Telegram profile ${name} must allow at least one user and chat`);
|
|
148
|
+
}
|
|
149
|
+
telegramProfiles[name] = { credential, allowedUserIds, allowedChatIds };
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
for (const [audience, rule] of Object.entries(audiences)) {
|
|
153
|
+
for (const channel of rule.channels) {
|
|
154
|
+
if (channel === "pi")
|
|
155
|
+
continue;
|
|
156
|
+
if (!channel.startsWith("telegram:") ||
|
|
157
|
+
telegramProfiles[channel.slice("telegram:".length)] === undefined) {
|
|
158
|
+
throw new Error(`Decision audience ${audience} references unknown channel ${channel}`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return {
|
|
163
|
+
schema: "pi-workflows.channels.v1",
|
|
164
|
+
audiences,
|
|
165
|
+
...(Object.keys(telegramProfiles).length === 0 ? {} : { telegramProfiles }),
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
export function parseCredentialConfig(value) {
|
|
169
|
+
const input = requireRecord(value, "Decision credential configuration");
|
|
170
|
+
if (input.schema !== "pi-workflows.credentials.v1") {
|
|
171
|
+
throw new Error("Decision credential configuration schema is invalid");
|
|
172
|
+
}
|
|
173
|
+
const telegramInput = requireRecord(input.telegram, "Telegram credentials");
|
|
174
|
+
const telegram = {};
|
|
175
|
+
for (const [name, raw] of Object.entries(telegramInput)) {
|
|
176
|
+
requireSimpleId(name, "Telegram credential");
|
|
177
|
+
const credential = requireRecord(raw, `Telegram credential ${name}`);
|
|
178
|
+
telegram[name] = { tokenFile: requireString(credential.tokenFile, "Telegram tokenFile") };
|
|
179
|
+
}
|
|
180
|
+
return { schema: "pi-workflows.credentials.v1", telegram };
|
|
181
|
+
}
|
|
182
|
+
async function requirePrivateFile(filePath, label) {
|
|
183
|
+
const stat = await fs.stat(filePath);
|
|
184
|
+
if (!stat.isFile())
|
|
185
|
+
throw new Error(`${label} must be a file`);
|
|
186
|
+
if ((stat.mode & 0o077) !== 0)
|
|
187
|
+
throw new Error(`${label} permissions must be 0600`);
|
|
188
|
+
}
|
|
189
|
+
async function readOptionalJson(filePath) {
|
|
190
|
+
try {
|
|
191
|
+
return JSON.parse(await fs.readFile(filePath, "utf8"));
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
if (error.code === "ENOENT")
|
|
195
|
+
return null;
|
|
196
|
+
throw error;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
async function writePrivateJson(filePath, value) {
|
|
200
|
+
const temporary = `${filePath}.${process.pid}.${randomUUID()}.tmp`;
|
|
201
|
+
await fs.writeFile(temporary, `${JSON.stringify(value, null, 2)}\n`, { mode: 0o600 });
|
|
202
|
+
await fs.rename(temporary, filePath);
|
|
203
|
+
await fs.chmod(filePath, 0o600);
|
|
204
|
+
}
|
|
205
|
+
function validateNumericIds(values, label) {
|
|
206
|
+
if (values.some((value) => !NUMERIC_ID.test(value))) {
|
|
207
|
+
throw new Error(`${label} must contain only numeric IDs`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
function requireSimpleId(value, label) {
|
|
211
|
+
const text = requireString(value, label);
|
|
212
|
+
if (!SIMPLE_ID.test(text))
|
|
213
|
+
throw new Error(`${label} is invalid`);
|
|
214
|
+
return text;
|
|
215
|
+
}
|
|
216
|
+
function requireString(value, label) {
|
|
217
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
218
|
+
throw new Error(`${label} must be a non-empty string`);
|
|
219
|
+
}
|
|
220
|
+
return value.trim();
|
|
221
|
+
}
|
|
222
|
+
function stringArray(value, label) {
|
|
223
|
+
if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) {
|
|
224
|
+
throw new Error(`${label} must be an array of strings`);
|
|
225
|
+
}
|
|
226
|
+
return [...value];
|
|
227
|
+
}
|
|
228
|
+
function requireRecord(value, label) {
|
|
229
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
230
|
+
throw new Error(`${label} must be an object`);
|
|
231
|
+
}
|
|
232
|
+
return value;
|
|
233
|
+
}
|
|
234
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/channels/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { HumanDecisionChannelRequest, HumanDecisionResponse } from "../workflows/types.js";
|
|
2
|
+
export declare const CHANNEL_ADAPTER_PROTOCOL_SCHEMA: "pi-workflows.channel-adapter.v1";
|
|
3
|
+
export type TelegramMessageReference = {
|
|
4
|
+
chatId: string;
|
|
5
|
+
messageId: string;
|
|
6
|
+
recipientIndex: number;
|
|
7
|
+
partIndex: number;
|
|
8
|
+
contentDigest: string;
|
|
9
|
+
};
|
|
10
|
+
type ChannelAdapterMessageBase = {
|
|
11
|
+
schema: typeof CHANNEL_ADAPTER_PROTOCOL_SCHEMA;
|
|
12
|
+
adapterEpoch: string;
|
|
13
|
+
profile: string;
|
|
14
|
+
sequence: number;
|
|
15
|
+
expectedRevision: number;
|
|
16
|
+
stableMessageId: string;
|
|
17
|
+
};
|
|
18
|
+
export type ChannelAdapterMessage = (ChannelAdapterMessageBase & {
|
|
19
|
+
kind: "channel.ready";
|
|
20
|
+
cursor: number;
|
|
21
|
+
}) | (ChannelAdapterMessageBase & {
|
|
22
|
+
kind: "channel.present";
|
|
23
|
+
decisionId: string;
|
|
24
|
+
requestDigest: string;
|
|
25
|
+
attemptId: string;
|
|
26
|
+
state: "confirmed" | "failed" | "unknown";
|
|
27
|
+
messages: TelegramMessageReference[];
|
|
28
|
+
errorCode?: string;
|
|
29
|
+
}) | (ChannelAdapterMessageBase & {
|
|
30
|
+
kind: "channel.answer";
|
|
31
|
+
decisionId: string;
|
|
32
|
+
requestDigest: string;
|
|
33
|
+
response: HumanDecisionResponse;
|
|
34
|
+
actorId: string;
|
|
35
|
+
chatId: string;
|
|
36
|
+
eventId: string;
|
|
37
|
+
idempotencyKey: string;
|
|
38
|
+
cursor: number;
|
|
39
|
+
}) | (ChannelAdapterMessageBase & {
|
|
40
|
+
kind: "channel.settle";
|
|
41
|
+
decisionId: string;
|
|
42
|
+
requestDigest: string;
|
|
43
|
+
attemptId: string;
|
|
44
|
+
state: "confirmed" | "failed" | "unknown";
|
|
45
|
+
errorCode?: string;
|
|
46
|
+
}) | (ChannelAdapterMessageBase & {
|
|
47
|
+
kind: "channel.exiting";
|
|
48
|
+
cursor: number;
|
|
49
|
+
});
|
|
50
|
+
export type ChannelAdapterCommand = {
|
|
51
|
+
kind: "channel.present";
|
|
52
|
+
stableMessageId: string;
|
|
53
|
+
attemptId: string;
|
|
54
|
+
request: HumanDecisionChannelRequest;
|
|
55
|
+
} | {
|
|
56
|
+
kind: "channel.settle";
|
|
57
|
+
stableMessageId: string;
|
|
58
|
+
attemptId: string;
|
|
59
|
+
request: HumanDecisionChannelRequest;
|
|
60
|
+
outcome: "accepted" | "cancelled" | "expired";
|
|
61
|
+
response?: HumanDecisionResponse;
|
|
62
|
+
messages: TelegramMessageReference[];
|
|
63
|
+
} | {
|
|
64
|
+
kind: "channel.poll";
|
|
65
|
+
cursor: number;
|
|
66
|
+
requests: HumanDecisionChannelRequest[];
|
|
67
|
+
} | {
|
|
68
|
+
kind: "channel.stop";
|
|
69
|
+
};
|
|
70
|
+
export type ChannelAdapterResponse = {
|
|
71
|
+
schema: typeof CHANNEL_ADAPTER_PROTOCOL_SCHEMA;
|
|
72
|
+
type: "response";
|
|
73
|
+
sequence: number;
|
|
74
|
+
outcome: "accepted" | "rejected";
|
|
75
|
+
revision: number;
|
|
76
|
+
command: ChannelAdapterCommand | null;
|
|
77
|
+
error?: string;
|
|
78
|
+
};
|
|
79
|
+
export type ChannelAdapterLaunch = {
|
|
80
|
+
schema: "pi-workflows.channel-adapter-launch.v1";
|
|
81
|
+
adapterEpoch: string;
|
|
82
|
+
profile: string;
|
|
83
|
+
token: string;
|
|
84
|
+
allowedUserIds: string[];
|
|
85
|
+
allowedChatIds: string[];
|
|
86
|
+
apiBase?: string;
|
|
87
|
+
};
|
|
88
|
+
export declare function encodeChannelLine(value: ChannelAdapterMessage | ChannelAdapterResponse): Buffer;
|
|
89
|
+
export declare function parseChannelAdapterMessage(frame: Buffer): ChannelAdapterMessage;
|
|
90
|
+
export declare function parseChannelAdapterResponse(frame: Buffer): ChannelAdapterResponse;
|
|
91
|
+
export declare function parseChannelAdapterLaunch(value: unknown): ChannelAdapterLaunch;
|
|
92
|
+
export declare function channelResponse(message: ChannelAdapterMessage, outcome: ChannelAdapterResponse["outcome"], revision: number, command: ChannelAdapterCommand | null, error?: string): ChannelAdapterResponse;
|
|
93
|
+
export declare function channelStableMessageId(parts: readonly string[]): string;
|
|
94
|
+
export {};
|