ag-ai 0.3.8 → 0.4.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/dist/k8s/runner.js +0 -3
- package/package.json +1 -1
- package/runtime/bridge.mjs +9 -16
- package/runtime/push.mjs +14 -31
package/dist/k8s/runner.js
CHANGED
|
@@ -40,8 +40,6 @@ const runnerStack = (context) => {
|
|
|
40
40
|
throw new Error('AG_AI_GENERATION_CIDR is required');
|
|
41
41
|
const imageProvider = context.env.AG_AI_IMAGE_PROVIDER ?? 'openai';
|
|
42
42
|
const opencodeProvider = context.env.AG_AI_OPENCODE_PROVIDER;
|
|
43
|
-
// '1' makes the bridge claim cloud work when the queue pushes it (see runtime/push.mjs).
|
|
44
|
-
const push = context.env.AG_AI_PUSH;
|
|
45
43
|
const agAiPackage = context.env.AG_AI_PACKAGE;
|
|
46
44
|
if (!agAiPackage)
|
|
47
45
|
throw new Error('AG_AI_PACKAGE is required');
|
|
@@ -95,7 +93,6 @@ const runnerStack = (context) => {
|
|
|
95
93
|
(0, common_1.env)('AG_AI_OPENCODE_URL', opencodeUrl),
|
|
96
94
|
(0, common_1.env)('AG_AI_OPENCODE_PASSWORD_FILE', secrets.opencode.file),
|
|
97
95
|
...(opencodeProvider === undefined ? [] : [(0, common_1.env)('AG_AI_OPENCODE_PROVIDER', opencodeProvider)]),
|
|
98
|
-
...(push === undefined ? [] : [(0, common_1.env)('AG_AI_PUSH', push)]),
|
|
99
96
|
(0, common_1.env)('AG_AI_IMAGE_PROVIDER', imageProvider),
|
|
100
97
|
(0, common_1.env)('AG_AI_IMAGE_API_KEY_FILE', secrets.image.file),
|
|
101
98
|
(0, common_1.env)('AG_AI_RUNNER_ID', common_1.RUNNER_APP),
|
package/package.json
CHANGED
package/runtime/bridge.mjs
CHANGED
|
@@ -828,10 +828,6 @@ const readConfig = async (env = process.env) => {
|
|
|
828
828
|
generationPort: env.AG_AI_GENERATION_PORT ?? DEFAULT_GENERATION_PORT,
|
|
829
829
|
generationStore: path.resolve(env.AG_AI_GENERATION_STORE ?? DEFAULT_GENERATION_STORE),
|
|
830
830
|
generationTokenFile: env.AG_AI_GENERATION_TOKEN_FILE ?? DEFAULT_GENERATION_TOKEN_FILE,
|
|
831
|
-
// AG_AI_PUSH=1 claims cloud work when the queue pushes it instead of every few seconds.
|
|
832
|
-
pushEnabled: env.AG_AI_PUSH === '1',
|
|
833
|
-
pushTicketPath: env.AG_AI_PUSH_TICKET_PATH,
|
|
834
|
-
pushHttpPath: env.AG_AI_PUSH_HTTP_PATH,
|
|
835
831
|
analyticaToken,
|
|
836
832
|
token,
|
|
837
833
|
};
|
|
@@ -979,17 +975,14 @@ const main = async () => {
|
|
|
979
975
|
process.once('SIGTERM', () => void stop('SIGTERM'));
|
|
980
976
|
process.once('SIGINT', () => void stop('SIGINT'));
|
|
981
977
|
|
|
982
|
-
|
|
983
|
-
const
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
log: (message) => console.log(`[runner] ${redact(message, [config.token])}`),
|
|
991
|
-
})
|
|
992
|
-
: undefined;
|
|
978
|
+
// The queue pushes queued work over a WebSocket; cloud claims follow pushes.
|
|
979
|
+
const workSignal = createWorkSignal();
|
|
980
|
+
const push = startPushChannel({
|
|
981
|
+
baseUrl: new URL(config.queueUrl).origin,
|
|
982
|
+
token: config.token,
|
|
983
|
+
onWork: () => workSignal.wake(),
|
|
984
|
+
log: (message) => console.log(`[runner] ${redact(message, [config.token])}`),
|
|
985
|
+
});
|
|
993
986
|
const heartbeatTimer = setInterval(() => void sendHeartbeat(), HEARTBEAT_INTERVAL_MS);
|
|
994
987
|
const healthTimer = setInterval(() => void refreshHealth(), HEALTH_INTERVAL_MS);
|
|
995
988
|
const claimLocal = async () => {
|
|
@@ -1076,7 +1069,7 @@ const main = async () => {
|
|
|
1076
1069
|
} finally {
|
|
1077
1070
|
clearInterval(heartbeatTimer);
|
|
1078
1071
|
clearInterval(healthTimer);
|
|
1079
|
-
push
|
|
1072
|
+
push.stop();
|
|
1080
1073
|
await stop('shutdown');
|
|
1081
1074
|
}
|
|
1082
1075
|
};
|
package/runtime/push.mjs
CHANGED
|
@@ -1,32 +1,23 @@
|
|
|
1
1
|
/* eslint-disable import/extensions -- Native Node ESM requires file extensions. */
|
|
2
2
|
// Push-driven cloud claims. The queue pushes "work queued" over a reactive
|
|
3
|
-
// WebSocket, so the runner claims only when there is work
|
|
4
|
-
//
|
|
3
|
+
// WebSocket, so the runner claims only when there is work. Every (re)connect
|
|
4
|
+
// delivers a fresh snapshot of queued work, so work queued while the socket was
|
|
5
|
+
// down is claimed on reconnect.
|
|
5
6
|
|
|
6
7
|
export const CLUSTER_WORK_PATH = 'cluster.work';
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export const DEFAULT_PUSH_HTTP_PATH = '/api/cluster/reactive';
|
|
8
|
+
const TICKET_PATH = '/api/cluster/token';
|
|
9
|
+
const HTTP_PATH = '/api/cluster/reactive';
|
|
10
10
|
|
|
11
|
-
/**
|
|
12
|
-
|
|
13
|
-
* keeps the original polling behaviour.
|
|
14
|
-
*/
|
|
15
|
-
export const createWorkSignal = ({
|
|
16
|
-
enabled,
|
|
17
|
-
fallbackMs = DEFAULT_PUSH_FALLBACK_MS,
|
|
18
|
-
now = Date.now,
|
|
19
|
-
}) => {
|
|
11
|
+
/** Decides when a cloud claim is worth a request: on start, and after each push. */
|
|
12
|
+
export const createWorkSignal = () => {
|
|
20
13
|
// Claim once at start: work may have been queued while the runner was down.
|
|
21
14
|
let pending = true;
|
|
22
|
-
let nextFallbackAt = 0;
|
|
23
15
|
let wakeWaiter;
|
|
24
16
|
return {
|
|
25
|
-
shouldClaim: () =>
|
|
26
|
-
/** A claim found no work: wait for
|
|
17
|
+
shouldClaim: () => pending,
|
|
18
|
+
/** A claim found no work: wait for the next push. */
|
|
27
19
|
drained: () => {
|
|
28
20
|
pending = false;
|
|
29
|
-
nextFallbackAt = now() + fallbackMs;
|
|
30
21
|
},
|
|
31
22
|
/** Work was pushed: claim on the next loop, waking an idle wait now. */
|
|
32
23
|
wake: () => {
|
|
@@ -49,18 +40,10 @@ export const createWorkSignal = ({
|
|
|
49
40
|
|
|
50
41
|
/**
|
|
51
42
|
* Holds the reactive WebSocket and calls `onWork` whenever the pushed snapshot
|
|
52
|
-
* lists queued work. Never throws: failures are logged and the
|
|
53
|
-
*
|
|
43
|
+
* lists queued work. Never throws: failures are logged and the connection keeps
|
|
44
|
+
* retrying, and the reconnect snapshot catches up on anything queued meanwhile.
|
|
54
45
|
*/
|
|
55
|
-
export const startPushChannel = ({
|
|
56
|
-
baseUrl,
|
|
57
|
-
token,
|
|
58
|
-
ticketPath = DEFAULT_PUSH_TICKET_PATH,
|
|
59
|
-
httpPath = DEFAULT_PUSH_HTTP_PATH,
|
|
60
|
-
onWork,
|
|
61
|
-
log,
|
|
62
|
-
connect,
|
|
63
|
-
}) => {
|
|
46
|
+
export const startPushChannel = ({ baseUrl, token, onWork, log, connect }) => {
|
|
64
47
|
const controller = new AbortController();
|
|
65
48
|
let client;
|
|
66
49
|
let subscription;
|
|
@@ -70,8 +53,8 @@ export const startPushChannel = ({
|
|
|
70
53
|
client = await connectClusterWorker({
|
|
71
54
|
baseUrl,
|
|
72
55
|
runnerToken: token,
|
|
73
|
-
ticketPath,
|
|
74
|
-
httpPath,
|
|
56
|
+
ticketPath: TICKET_PATH,
|
|
57
|
+
httpPath: HTTP_PATH,
|
|
75
58
|
signal: controller.signal,
|
|
76
59
|
onRetry: (error) => log(`push connect failed; retrying: ${error.message}`),
|
|
77
60
|
client: { onError: (error) => log(`push socket error: ${error.message}`) },
|