@agentshouse/kit 0.2.1-c2c600f2d2b30 → 0.2.1-c30728be20485
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/dist/api.js +19 -1
- package/dist/bootstrap.json +4 -4
- package/dist/bridge.js +24 -17
- package/dist/copies.js +3 -3
- package/dist/refusals.js +12 -0
- package/dist/secret-input.js +2 -2
- package/dist/stream.js +3 -3
- package/package.json +1 -1
package/dist/api.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { setTimeout as delay } from 'node:timers/promises';
|
|
1
2
|
import { end, SIGN_IN_AGAIN } from './end.js';
|
|
2
3
|
import { readEnrolment } from './home.js';
|
|
4
|
+
import { retryable } from './refusals.js';
|
|
3
5
|
import { relayed } from './relay.js';
|
|
4
6
|
export class HouseRefusal extends Error {
|
|
5
7
|
status;
|
|
@@ -10,10 +12,26 @@ export class HouseRefusal extends Error {
|
|
|
10
12
|
this.text = text;
|
|
11
13
|
}
|
|
12
14
|
}
|
|
15
|
+
export function finalRefusal(error) {
|
|
16
|
+
return error instanceof HouseRefusal && retryable(error.text) === false;
|
|
17
|
+
}
|
|
13
18
|
export function retryDelay(attempt) {
|
|
14
19
|
// Full jitter, doubling from one second to a 30-second cap, spreads the Kits one outage dropped and retries within half a minute of House's return.
|
|
15
20
|
return Math.random() * Math.min(30_000, 1000 * 2 ** attempt);
|
|
16
21
|
}
|
|
22
|
+
export async function retrying(sent, signal) {
|
|
23
|
+
for (let attempt = 0;; attempt++) {
|
|
24
|
+
try {
|
|
25
|
+
return await sent();
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
if (!(error instanceof HouseRefusal) || retryable(error.text) !== true)
|
|
29
|
+
throw error;
|
|
30
|
+
}
|
|
31
|
+
// A request House refused as retryable is sent again on the jittered curve.
|
|
32
|
+
await delay(retryDelay(attempt), undefined, { signal });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
17
35
|
export async function post(house, path, body, credential, signal) {
|
|
18
36
|
const answer = await fetch(relayed(new URL(path, house)), {
|
|
19
37
|
method: 'POST',
|
|
@@ -48,7 +66,7 @@ export const house = {
|
|
|
48
66
|
return await enrolledPost(path, body);
|
|
49
67
|
}
|
|
50
68
|
catch (error) {
|
|
51
|
-
if (error
|
|
69
|
+
if (finalRefusal(error))
|
|
52
70
|
throw error;
|
|
53
71
|
process.stderr.write(`kit: ${path} did not reach House: ${error.message}\n`);
|
|
54
72
|
// A delivery House did not take is sent again on the jittered curve for as long as it is still current.
|
package/dist/bootstrap.json
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 2,
|
|
3
|
-
"version": "0.2.1-
|
|
3
|
+
"version": "0.2.1-c30728be20485",
|
|
4
4
|
"hosts": {
|
|
5
5
|
"macos": {
|
|
6
6
|
"asset": {
|
|
7
7
|
"name": "connect-linux.sh",
|
|
8
|
-
"url": "https://github.com/agentshouse/kit/releases/download/v0.2.1-
|
|
8
|
+
"url": "https://github.com/agentshouse/kit/releases/download/v0.2.1-c30728be20485/connect-linux.sh"
|
|
9
9
|
}
|
|
10
10
|
},
|
|
11
11
|
"linux": {
|
|
12
12
|
"asset": {
|
|
13
13
|
"name": "connect-linux.sh",
|
|
14
|
-
"url": "https://github.com/agentshouse/kit/releases/download/v0.2.1-
|
|
14
|
+
"url": "https://github.com/agentshouse/kit/releases/download/v0.2.1-c30728be20485/connect-linux.sh"
|
|
15
15
|
}
|
|
16
16
|
},
|
|
17
17
|
"windows": {
|
|
18
18
|
"asset": {
|
|
19
19
|
"name": "connect-windows.ps1",
|
|
20
|
-
"url": "https://github.com/agentshouse/kit/releases/download/v0.2.1-
|
|
20
|
+
"url": "https://github.com/agentshouse/kit/releases/download/v0.2.1-c30728be20485/connect-windows.ps1"
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
}
|
package/dist/bridge.js
CHANGED
|
@@ -6,13 +6,13 @@ import { createServer, request as httpRequest } from 'node:http';
|
|
|
6
6
|
import { request as httpsRequest } from 'node:https';
|
|
7
7
|
import { delimiter, dirname, join } from 'node:path';
|
|
8
8
|
import { parse } from 'yaml';
|
|
9
|
-
import { HouseRefusal, retryDelay } from './api.js';
|
|
9
|
+
import { finalRefusal, HouseRefusal, retryDelay, retrying } from './api.js';
|
|
10
10
|
import { loginPath } from './clis.js';
|
|
11
11
|
import { changedPaths, } from './copies.js';
|
|
12
12
|
import { kitHome, readEnrolment } from './home.js';
|
|
13
13
|
import { callHouse, mcpBody, rpc, UNREACHABLE } from './mcp.js';
|
|
14
14
|
import { operationId, uuidOf } from './operation.js';
|
|
15
|
-
import { refusalOf } from './refusals.js';
|
|
15
|
+
import { refusalOf, retryable } from './refusals.js';
|
|
16
16
|
import { relayed } from './relay.js';
|
|
17
17
|
const MCP_REQUEST_BYTES = 4 * 1024 * 1024;
|
|
18
18
|
const ATTACHMENTS = '/kit/attachments/upload';
|
|
@@ -40,8 +40,6 @@ function refusalIn(text, status) {
|
|
|
40
40
|
return refusalOf(text) ?? { code: '', text: text === '' ? `House refused it with ${status}` : text, conflicts: [] };
|
|
41
41
|
}
|
|
42
42
|
function answered(forwarded) {
|
|
43
|
-
if (forwarded.status >= 500)
|
|
44
|
-
throw new HouseRefusal('/', forwarded.status, forwarded.text);
|
|
45
43
|
if (forwarded.status !== 200)
|
|
46
44
|
return { refused: refusalIn(forwarded.text, forwarded.status) };
|
|
47
45
|
const answer = JSON.parse(forwarded.text);
|
|
@@ -65,8 +63,8 @@ function pendingSeconds(forwarded) {
|
|
|
65
63
|
function resendAfter(forwarded, attempt) {
|
|
66
64
|
if (forwarded === null)
|
|
67
65
|
return retryDelay(attempt);
|
|
68
|
-
if (forwarded.status
|
|
69
|
-
return
|
|
66
|
+
if (forwarded.status !== 200)
|
|
67
|
+
return retryable(forwarded.text) === false ? null : retryDelay(attempt);
|
|
70
68
|
const seconds = pendingSeconds(forwarded);
|
|
71
69
|
return seconds === null ? null : seconds * 1000;
|
|
72
70
|
}
|
|
@@ -120,17 +118,26 @@ export async function openBridge(house, copies, conversationId, signal) {
|
|
|
120
118
|
if ('refused' in prepared)
|
|
121
119
|
return prepared;
|
|
122
120
|
const { transfer } = parse(prepared.text);
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
121
|
+
let text;
|
|
122
|
+
try {
|
|
123
|
+
text = await retrying(async () => {
|
|
124
|
+
const posted = await fetch(relayed(transfer.url), {
|
|
125
|
+
method: 'POST',
|
|
126
|
+
headers: { 'content-type': 'text/plain; charset=utf-8' },
|
|
127
|
+
body: Buffer.concat([Buffer.from(`${transfer.operation}\n`), bytes]),
|
|
128
|
+
signal: closed.signal,
|
|
129
|
+
});
|
|
130
|
+
const answer = await posted.text();
|
|
131
|
+
if (!posted.ok)
|
|
132
|
+
throw new HouseRefusal('/', posted.status, answer);
|
|
133
|
+
return answer;
|
|
134
|
+
}, closed.signal);
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
if (!finalRefusal(error))
|
|
138
|
+
throw error;
|
|
139
|
+
return { refused: refusalIn(error.text, error.status) };
|
|
140
|
+
}
|
|
134
141
|
const held = JSON.parse(text).transfer;
|
|
135
142
|
return { url: held.url, operation: held.operation, bytes: bytes.length, sha256 };
|
|
136
143
|
};
|
package/dist/copies.js
CHANGED
|
@@ -2,7 +2,7 @@ import { parse } from '@agentshouse/mdmodel';
|
|
|
2
2
|
import { randomUUID } from 'node:crypto';
|
|
3
3
|
import { mkdir, readFile, readdir, realpath, rename, rm, rmdir, stat, writeFile } from 'node:fs/promises';
|
|
4
4
|
import { dirname, join } from 'node:path';
|
|
5
|
-
import { HouseRefusal } from './api.js';
|
|
5
|
+
import { finalRefusal, HouseRefusal, retrying } from './api.js';
|
|
6
6
|
import { blobId, blobText, git, HOUSE_REF, importCommits, localCopies, revision, run, succeeded, tree, } from './git.js';
|
|
7
7
|
import { kitHome } from './home.js';
|
|
8
8
|
import { operationId } from './operation.js';
|
|
@@ -160,11 +160,11 @@ export class LocalCopies {
|
|
|
160
160
|
key: 'owner',
|
|
161
161
|
submit: async (operation, changes) => {
|
|
162
162
|
try {
|
|
163
|
-
const outcome = await house.post('/kit/door/batch', { operation_id: operation, changes });
|
|
163
|
+
const outcome = await retrying(() => house.post('/kit/door/batch', { operation_id: operation, changes }));
|
|
164
164
|
return { accepted: outcome.contents };
|
|
165
165
|
}
|
|
166
166
|
catch (error) {
|
|
167
|
-
if (!(error
|
|
167
|
+
if (!finalRefusal(error))
|
|
168
168
|
throw error;
|
|
169
169
|
return {
|
|
170
170
|
refused: refusalOf(error.text) ?? { code: '', text: `House refused it with ${error.status}`, conflicts: [] },
|
package/dist/refusals.js
CHANGED
|
@@ -21,6 +21,18 @@ function spoken(text) {
|
|
|
21
21
|
const detail = said.indexOf('\n');
|
|
22
22
|
return { code: head[1], text: said, conflicts: detail === -1 ? [] : conflictCodes(said.slice(detail + 1)) };
|
|
23
23
|
}
|
|
24
|
+
export function retryable(text) {
|
|
25
|
+
let body;
|
|
26
|
+
try {
|
|
27
|
+
body = JSON.parse(text);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
const error = body?.error;
|
|
33
|
+
const verdict = error?.retryable ?? error?.data?.retryable;
|
|
34
|
+
return typeof verdict === 'boolean' ? verdict : null;
|
|
35
|
+
}
|
|
24
36
|
export function refusalOf(text) {
|
|
25
37
|
let body;
|
|
26
38
|
try {
|
package/dist/secret-input.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { finalRefusal, retryDelay } from './api.js';
|
|
2
2
|
export async function holdSecretInput(house, subject, steps, waiting) {
|
|
3
3
|
for (let failures = 0; waiting();) {
|
|
4
4
|
try {
|
|
@@ -10,7 +10,7 @@ export async function holdSecretInput(house, subject, steps, waiting) {
|
|
|
10
10
|
failures = 0;
|
|
11
11
|
}
|
|
12
12
|
catch (error) {
|
|
13
|
-
if (error
|
|
13
|
+
if (finalRefusal(error))
|
|
14
14
|
return null;
|
|
15
15
|
process.stderr.write(`kit: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
16
16
|
// A wait that did not reach House is held again on the jittered curve for as long as its asker still waits.
|
package/dist/stream.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { subscribe } from 'node:diagnostics_channel';
|
|
2
|
-
import { HouseRefusal, retryDelay } from './api.js';
|
|
2
|
+
import { finalRefusal, HouseRefusal, retryDelay } from './api.js';
|
|
3
3
|
import { end, REPLACED, SIGN_IN_AGAIN } from './end.js';
|
|
4
4
|
import { readEnrolment } from './home.js';
|
|
5
5
|
import { relayed } from './relay.js';
|
|
@@ -54,9 +54,9 @@ export function holdStream(handlers) {
|
|
|
54
54
|
end(REPLACED);
|
|
55
55
|
if (event.reason === 'credential_rejected' || refused?.status === 401)
|
|
56
56
|
end(SIGN_IN_AGAIN);
|
|
57
|
-
if (refused
|
|
57
|
+
if (finalRefusal(refused))
|
|
58
58
|
end(refused.message);
|
|
59
|
-
// A closed socket reopens in one to three seconds, spread so Kits one restart closed return apart;
|
|
59
|
+
// A closed socket reopens in one to three seconds, spread so Kits one restart closed return apart; an opening House refused as retryable or left unanswered backs off on the jittered curve.
|
|
60
60
|
setTimeout(() => void connect(), opened ? 1000 + Math.random() * 2000 : retryDelay(failures++));
|
|
61
61
|
};
|
|
62
62
|
};
|