nofax 0.2.4 → 0.2.6
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/package.json +1 -1
- package/src/mcp-server.mjs +2 -2
- package/src/mcp-tools.mjs +23 -11
- package/src/ntfy.mjs +5 -2
package/package.json
CHANGED
package/src/mcp-server.mjs
CHANGED
|
@@ -74,7 +74,7 @@ const LIST_PENDING_OUTPUT_SCHEMA = z.object({
|
|
|
74
74
|
|
|
75
75
|
export function buildMcpServer({ handlers = createMcpToolHandlers() } = {}) {
|
|
76
76
|
const server = new McpServer(
|
|
77
|
-
{ name: 'nofax', version: '0.2.
|
|
77
|
+
{ name: 'nofax', version: '0.2.6' },
|
|
78
78
|
{ instructions: SERVER_INSTRUCTIONS }
|
|
79
79
|
);
|
|
80
80
|
|
|
@@ -156,7 +156,7 @@ export function buildMcpServer({ handlers = createMcpToolHandlers() } = {}) {
|
|
|
156
156
|
waitSeconds: z.number().int().min(1).max(240).optional().describe('Maximum seconds to long-poll during this call; defaults to 240. A timeout still returns pending, never approval.')
|
|
157
157
|
}),
|
|
158
158
|
outputSchema: WAIT_OUTPUT_SCHEMA,
|
|
159
|
-
annotations: { readOnlyHint:
|
|
159
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true }
|
|
160
160
|
},
|
|
161
161
|
async (args) => result(await handlers.waitForResponse(args))
|
|
162
162
|
);
|
package/src/mcp-tools.mjs
CHANGED
|
@@ -140,6 +140,17 @@ export function createMcpToolHandlers(overrides = {}) {
|
|
|
140
140
|
return pendingResult(remote.requestId);
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
+
async function createDurableRemote(kind, input) {
|
|
144
|
+
let persisted;
|
|
145
|
+
const remote = await deps.createRemoteRequestImpl({
|
|
146
|
+
...input,
|
|
147
|
+
beforePublish: async (prepared) => {
|
|
148
|
+
persisted = await persistRemote({ kind, remote: prepared });
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
return persisted ?? persistRemote({ kind, remote });
|
|
152
|
+
}
|
|
153
|
+
|
|
143
154
|
return {
|
|
144
155
|
async notify({ title = 'Nofax', message }) {
|
|
145
156
|
const current = await config();
|
|
@@ -153,7 +164,7 @@ export function createMcpToolHandlers(overrides = {}) {
|
|
|
153
164
|
|
|
154
165
|
async requestApproval({ title = 'Nofax approval', message, allowRefine = false }) {
|
|
155
166
|
const current = await config();
|
|
156
|
-
|
|
167
|
+
return createDurableRemote('approval', {
|
|
157
168
|
config: current,
|
|
158
169
|
title: validateText(title, 'TITLE', 120),
|
|
159
170
|
message: validateText(message, 'MESSAGE'),
|
|
@@ -163,32 +174,29 @@ export function createMcpToolHandlers(overrides = {}) {
|
|
|
163
174
|
],
|
|
164
175
|
includeRefine: allowRefine === true
|
|
165
176
|
});
|
|
166
|
-
return persistRemote({ kind: 'approval', remote });
|
|
167
177
|
},
|
|
168
178
|
|
|
169
179
|
async requestChoice({ title = 'Nofax choice', message, options }) {
|
|
170
180
|
const current = await config();
|
|
171
181
|
const normalized = validateChoiceOptions(options);
|
|
172
|
-
|
|
182
|
+
return createDurableRemote('choice', {
|
|
173
183
|
config: current,
|
|
174
184
|
title: validateText(title, 'TITLE', 120),
|
|
175
185
|
message: validateText(message, 'MESSAGE'),
|
|
176
186
|
options: normalized,
|
|
177
187
|
includeRefine: false
|
|
178
188
|
});
|
|
179
|
-
return persistRemote({ kind: 'choice', remote });
|
|
180
189
|
},
|
|
181
190
|
|
|
182
191
|
async requestRefinement({ title = 'Nofax refinement', message }) {
|
|
183
192
|
const current = await config();
|
|
184
|
-
|
|
193
|
+
return createDurableRemote('refinement', {
|
|
185
194
|
config: current,
|
|
186
195
|
title: validateText(title, 'TITLE', 120),
|
|
187
196
|
message: validateText(message, 'MESSAGE'),
|
|
188
197
|
options: [],
|
|
189
198
|
includeRefine: true
|
|
190
199
|
});
|
|
191
|
-
return persistRemote({ kind: 'refinement', remote });
|
|
192
200
|
},
|
|
193
201
|
|
|
194
202
|
async waitForResponse({ requestId, waitSeconds }) {
|
|
@@ -213,11 +221,15 @@ export function createMcpToolHandlers(overrides = {}) {
|
|
|
213
221
|
response,
|
|
214
222
|
resolvedAt: new Date(deps.nowImpl()).toISOString()
|
|
215
223
|
});
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
224
|
+
const acceptedResponse = request.decision === response.decision
|
|
225
|
+
&& (request.text ?? undefined) === (response.text ?? undefined);
|
|
226
|
+
if (acceptedResponse) {
|
|
227
|
+
await deps.sendResponseConfirmationImpl({
|
|
228
|
+
config: current,
|
|
229
|
+
response,
|
|
230
|
+
title: request.kind === 'approval' ? 'Approval' : request.kind === 'refinement' ? 'Refinement' : 'Choice'
|
|
231
|
+
});
|
|
232
|
+
}
|
|
221
233
|
return terminalResult(request);
|
|
222
234
|
}
|
|
223
235
|
const remaining = deadline - deps.nowImpl();
|
package/src/ntfy.mjs
CHANGED
|
@@ -96,6 +96,7 @@ export async function createRemoteRequest({
|
|
|
96
96
|
options = [],
|
|
97
97
|
includeRefine = false,
|
|
98
98
|
shortcutName = config.refineShortcutName ?? DEFAULT_REFINE_SHORTCUT,
|
|
99
|
+
beforePublish = async () => {},
|
|
99
100
|
fetchImpl = fetch
|
|
100
101
|
}) {
|
|
101
102
|
const normalized = normalizeOptions(options);
|
|
@@ -126,8 +127,10 @@ export async function createRemoteRequest({
|
|
|
126
127
|
}
|
|
127
128
|
}
|
|
128
129
|
|
|
130
|
+
const remote = { requestId, responseTopic, allowed };
|
|
131
|
+
await beforePublish(remote);
|
|
129
132
|
await sendNotification({ config, title, message, actions, fetchImpl });
|
|
130
|
-
return
|
|
133
|
+
return remote;
|
|
131
134
|
}
|
|
132
135
|
|
|
133
136
|
function parseNtfyPoll(text, requestId, allowed) {
|
|
@@ -149,7 +152,7 @@ function parseNtfyPoll(text, requestId, allowed) {
|
|
|
149
152
|
export async function pollRemoteResponse({ config, responseTopic, requestId, allowed, fetchImpl = fetch }) {
|
|
150
153
|
let response;
|
|
151
154
|
try {
|
|
152
|
-
response = await fetchImpl(`${config.server}/${responseTopic}/json?poll=1&since=
|
|
155
|
+
response = await fetchImpl(`${config.server}/${responseTopic}/json?poll=1&since=all`, {
|
|
153
156
|
method: 'GET',
|
|
154
157
|
headers: { accept: 'application/x-ndjson' }
|
|
155
158
|
});
|