@volter/supercode-teams 0.3.121 → 0.3.123
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/bin/teams.mjs +6 -1
- package/bin/workspace.mjs +17 -2
- package/package.json +1 -1
- package/worker/index.mjs +33 -6
package/bin/teams.mjs
CHANGED
|
@@ -187,7 +187,12 @@ async function main() {
|
|
|
187
187
|
process.on('SIGTERM', stop);
|
|
188
188
|
return;
|
|
189
189
|
}
|
|
190
|
-
if (sub === 'status') {
|
|
190
|
+
if (sub === 'status') {
|
|
191
|
+
// and every supercode process here still running a release an install replaced (t_8e109edc)
|
|
192
|
+
let stale_releases = null;
|
|
193
|
+
try { stale_releases = await (await import('@volter/supercode-health/drift')).staleSupercode(); } catch { /* not read */ }
|
|
194
|
+
out({ ...readJson(p.machine, { running: false }), stale_releases }); return;
|
|
195
|
+
}
|
|
191
196
|
// `machine restart [--machine M] [--reason text]`: the daemon (this one, or M's through the server) restarts under its service.
|
|
192
197
|
if (sub === 'restart') return withNode(async (rpc) => out(await rpc.call('machine.restart', { reason: flags.reason ? String(flags.reason) : null })));
|
|
193
198
|
die('machine start|status|restart');
|
package/bin/workspace.mjs
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
import { StringDecoder } from 'node:string_decoder';
|
|
3
3
|
import { hostname } from 'node:os';
|
|
4
4
|
import { agentSessionHint } from './workspace/agent-session.mjs';
|
|
5
|
-
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
6
6
|
import { existsSync, readFileSync, realpathSync, rmSync } from 'node:fs';
|
|
7
|
-
import { basename, join, resolve } from 'node:path';
|
|
7
|
+
import { basename, dirname, join, resolve } from 'node:path';
|
|
8
8
|
import { execFileSync, spawn } from 'node:child_process';
|
|
9
9
|
import { createHash } from 'node:crypto';
|
|
10
10
|
import { SupercodeHarnessClient } from '@volter/supercode-harness-sdk';
|
|
@@ -1119,6 +1119,7 @@ async function connectorCommand(verb, { flags, rest, output, client, context, te
|
|
|
1119
1119
|
if (!flags.json) {
|
|
1120
1120
|
process.stdout.write(`Connected ${saved.machine.name} to team ${context.team_name ?? context.team_id}; it stays connected (a ${service.kind ?? 'background'} service).\n`);
|
|
1121
1121
|
for (const line of service.message_tools ?? []) process.stdout.write(` ${line}\n`);
|
|
1122
|
+
for (const line of await releaseSkills()) process.stdout.write(` ${line}\n`);
|
|
1122
1123
|
return;
|
|
1123
1124
|
}
|
|
1124
1125
|
return output({ version: 1, context: scope(context), data: { machine: saved.machine, source: saved.source, service, ...(addedRule ? { rule: addedRule } : {}) } });
|
|
@@ -1486,3 +1487,17 @@ function stampStderr() {
|
|
|
1486
1487
|
};
|
|
1487
1488
|
process.stderr.__stamped = true;
|
|
1488
1489
|
}
|
|
1490
|
+
|
|
1491
|
+
/**
|
|
1492
|
+
* The skills of the release this Teams package belongs to (`@volter/supercode`'s lib/skills.mjs, the package that
|
|
1493
|
+
* holds it), put into every harness here: a machine connected to a team has agents that know supercode
|
|
1494
|
+
* (t_266912ff). A Teams package run from a checkout or installed alone has no release beside it, and installs none.
|
|
1495
|
+
*/
|
|
1496
|
+
async function releaseSkills() {
|
|
1497
|
+
const release = join(dirname(fileURLToPath(import.meta.url)), '..', '..', '..', '..');
|
|
1498
|
+
try {
|
|
1499
|
+
if (JSON.parse(readFileSync(join(release, 'package.json'), 'utf8')).name !== '@volter/supercode') return [];
|
|
1500
|
+
const { installSkills } = await import(pathToFileURL(join(release, 'lib', 'skills.mjs')).href);
|
|
1501
|
+
return installSkills();
|
|
1502
|
+
} catch { return []; }
|
|
1503
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/supercode-teams",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.123",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A machine daemon serving this machine's doors, and a local or remote Teams server for shared session discovery, OpenTelemetry ingestion and access to enrolled machines.",
|
|
6
6
|
"license": "MIT",
|
package/worker/index.mjs
CHANGED
|
@@ -143,15 +143,42 @@ export class TeamsServer extends DurableObject {
|
|
|
143
143
|
}
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
+
/** What the Durable Object's runtime said of a failed call (its error's own flags), for the log and the answer. */
|
|
147
|
+
const objectFailure = (error) => ({
|
|
148
|
+
name: String(error?.name ?? 'Error'),
|
|
149
|
+
message: String(error?.message ?? error).slice(0, 200),
|
|
150
|
+
retryable: error?.retryable === true,
|
|
151
|
+
overloaded: error?.overloaded === true,
|
|
152
|
+
remote: error?.remote === true,
|
|
153
|
+
reset: error?.durableObjectReset === true,
|
|
154
|
+
});
|
|
155
|
+
|
|
146
156
|
export default {
|
|
147
157
|
async fetch(request, env) {
|
|
148
|
-
|
|
158
|
+
const repeatable = request.method === 'GET' || request.method === 'HEAD';
|
|
159
|
+
const call = () => env.TEAMS_SERVER.get(env.TEAMS_SERVER.idFromName('server')).fetch(repeatable ? request.clone() : request);
|
|
160
|
+
// A request safe to repeat (a read, or a link's WebSocket upgrade: every machine's connection) is tried once more
|
|
161
|
+
// when the runtime says the failure is retryable and the object is not overloaded: a reset object (its code
|
|
162
|
+
// updated, or evicted and restarted) answers the second call. Fleet-wide 503s for minutes with no deploy behind
|
|
163
|
+
// them (2026-10-06, 14:04Z and 18:41-18:44Z) dropped every machine's link; what the runtime said of them was only
|
|
164
|
+
// in the log, which now says it in one JSON line and the answer names it.
|
|
165
|
+
let first = null;
|
|
166
|
+
try { return await call(); }
|
|
149
167
|
catch (error) {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
const requestId = crypto.randomUUID();
|
|
153
|
-
console.error(`Teams request ${requestId} failed before response: ${error.message}`);
|
|
154
|
-
return Response.json({ version: 1, error: { code: 'unavailable', message: 'Teams server is temporarily unavailable', request_id: requestId, retryable: true } }, { status: 503, headers: { 'Retry-After': '5', 'X-Request-ID': requestId } });
|
|
168
|
+
first = objectFailure(error);
|
|
169
|
+
if (!(repeatable && first.retryable && !first.overloaded)) return unavailable(first, null);
|
|
155
170
|
}
|
|
171
|
+
try { return await call(); }
|
|
172
|
+
catch (error) { return unavailable(objectFailure(error), first); }
|
|
156
173
|
},
|
|
157
174
|
};
|
|
175
|
+
|
|
176
|
+
/** Failures before the HTTP handler (object initialization, migrations, storage, a reset) as our API contract, not a
|
|
177
|
+
* platform HTML page. The answer goes to callers not yet authenticated, so it names only the error's kind and the
|
|
178
|
+
* runtime's flags; its message (storage or SQL text, internal paths) is in the log line its request id leads to. */
|
|
179
|
+
function unavailable(failure, retried) {
|
|
180
|
+
const requestId = crypto.randomUUID();
|
|
181
|
+
console.error(JSON.stringify({ event: 'teams_unavailable', request_id: requestId, failure, ...(retried ? { first: retried } : {}) }));
|
|
182
|
+
const cause = `${failure.name}${failure.reset ? ', object reset' : ''}${failure.overloaded ? ', overloaded' : ''}${retried ? ', after one retry' : ''}`;
|
|
183
|
+
return Response.json({ version: 1, error: { code: 'unavailable', message: `Teams server is temporarily unavailable (${cause})`, request_id: requestId, retryable: true, cause: { name: failure.name, retryable: failure.retryable, overloaded: failure.overloaded, remote: failure.remote, reset: failure.reset } } }, { status: 503, headers: { 'Retry-After': failure.overloaded ? '15' : '5', 'X-Request-ID': requestId } });
|
|
184
|
+
}
|