@moxxy/cli 0.13.0 → 0.13.2
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/bin.js +856 -377
- package/dist/bin.js.map +1 -1
- package/package.json +2 -2
package/dist/bin.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import { z as z$1, createMutex, defineTunnelProvider,
|
|
3
|
+
import { z as z$1, createMutex, defineTunnelProvider, definePlugin, defineProvider, defineTool, MoxxyError, asTurnId, defineMode, asPluginId, defineCommand, defineChannel, defineWorkflowExecutor, toFriendlyError, estimateTextTokens, classifyHttpStatus, createStuckLoopDetector, runCompactionIfNeeded, runElisionIfNeeded, collectProviderStream, usageEventFields, isContextOverflowError, emitRequestsAndDetectStuck, executeToolUses, buildSystemPromptWithSkills, projectMessages, defineCompactor, defineCacheStrategy, denyByDefaultResolver, createAllowListResolver, zodToJsonSchema, fileDiffSummary, runSingleShotTurn, defineSurface, runManualCompaction, isFileDiffDisplay, renderFrontmatter, defineEmbedder, migrateModeName, skillFrontmatterSchema, asSkillId, getInstallHint, parseFrontmatterFile, createDeferredPermissionResolver, encodeLoginPrompt, defineTranscriber, summarizeTokensByModel, countNodes, moxxyPackageSchema, classifyNetworkError, addModelTotals, createJsonFileStore, ISOLATION_RANK, MOXXY_PCM16_24KHZ_MIME, fileDiffVerb, parseFrontmatter, createCallbackResolver, autoAllowResolver, asSessionId, asToolCallId, defineViewRenderer, DEFAULT_VIEW_TAGS, assertNever, isSafeViewUrl, evaluateToolRule, summarizeSessionTokensFromEvents, toDiffRows, diffGutterNo, computeElisionState, toolResultStubbed, toolResultStub, toolResultBytes, conversationalStubbed, conversationalStub, asEventId } from '@moxxy/sdk';
|
|
4
4
|
import * as fs32 from 'fs';
|
|
5
|
-
import fs32__default, { existsSync, promises, ReadStream, mkdirSync, statSync, readdirSync, writeFileSync, readFileSync,
|
|
5
|
+
import fs32__default, { existsSync, promises, ReadStream, mkdirSync, statSync, readdirSync, writeFileSync, readFileSync, unlinkSync, chmodSync, watch, createReadStream } from 'fs';
|
|
6
6
|
import * as path3 from 'path';
|
|
7
7
|
import path3__default, { join, dirname, basename } from 'path';
|
|
8
|
+
import { isCliTunnelAvailable, writeFileAtomic, spawnCliTunnel, moxxyPath, moxxyHome, bearerTokenMatches, resolveChannelToken, rotateChannelToken, readRequestBody, MOXXY_WS_SUBPROTOCOL, bearerGuard, tokenFromWsProtocolHeader, writeFileAtomicSync } from '@moxxy/sdk/server';
|
|
8
9
|
import { z } from 'zod';
|
|
9
10
|
import * as os5 from 'os';
|
|
10
11
|
import os5__default, { homedir, tmpdir, userInfo } from 'os';
|
|
@@ -14,7 +15,7 @@ import Stream, { Readable as Readable$1, PassThrough as PassThrough$2, Stream as
|
|
|
14
15
|
import http, { createServer } from 'http';
|
|
15
16
|
import https from 'https';
|
|
16
17
|
import zlib from 'zlib';
|
|
17
|
-
import { webcrypto, randomBytes, createHash,
|
|
18
|
+
import { randomUUID, webcrypto, randomBytes, createHash, scryptSync, createCipheriv, createDecipheriv, createHmac, timingSafeEqual } from 'crypto';
|
|
18
19
|
import 'zod/v3';
|
|
19
20
|
import * as z4mini from 'zod/v4-mini';
|
|
20
21
|
import * as z53 from 'zod/v4';
|
|
@@ -223,6 +224,8 @@ var init_log = __esm({
|
|
|
223
224
|
this.now = opts.now ?? Date.now;
|
|
224
225
|
for (const e3 of seed)
|
|
225
226
|
this.events.push(e3);
|
|
227
|
+
if (seed.length > 0)
|
|
228
|
+
this.base = seed[0].seq;
|
|
226
229
|
}
|
|
227
230
|
get length() {
|
|
228
231
|
return this.events.length;
|
|
@@ -447,15 +450,20 @@ async function streamChildEventToParent(parentSession, parentTurnId, label3, chi
|
|
|
447
450
|
const mapped = mapChildEvent(label3, childSessionId, childEvt);
|
|
448
451
|
if (!mapped)
|
|
449
452
|
return;
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
453
|
+
try {
|
|
454
|
+
await parentSession.log.append({
|
|
455
|
+
type: "plugin_event",
|
|
456
|
+
sessionId: parentSession.id,
|
|
457
|
+
turnId: parentTurnId,
|
|
458
|
+
source: "plugin",
|
|
459
|
+
pluginId: SUBAGENT_PLUGIN_ID,
|
|
460
|
+
subtype: mapped.subtype,
|
|
461
|
+
payload: mapped.payload
|
|
462
|
+
});
|
|
463
|
+
} catch (err) {
|
|
464
|
+
process.stderr.write(`moxxy: dropped subagent progress event (${mapped.subtype}) \u2014 parent log append failed: ${err instanceof Error ? err.message : String(err)}
|
|
465
|
+
`);
|
|
466
|
+
}
|
|
459
467
|
}
|
|
460
468
|
function mapChildEvent(label3, childSessionId, childEvt) {
|
|
461
469
|
const payload = {
|
|
@@ -1561,7 +1569,16 @@ var init_providers = __esm({
|
|
|
1561
1569
|
if (instance)
|
|
1562
1570
|
this.instances.set(def.name, instance);
|
|
1563
1571
|
}
|
|
1564
|
-
/**
|
|
1572
|
+
/**
|
|
1573
|
+
* Overwrite an existing def (also drops the cached instance so the new
|
|
1574
|
+
* createClient gets called).
|
|
1575
|
+
*
|
|
1576
|
+
* Invariant: replacing the ACTIVE provider's def leaves `active` pointing at
|
|
1577
|
+
* it but with no cached instance, so `getActive()` throws until the caller
|
|
1578
|
+
* rebuilds the instance. Callers replacing the active provider MUST follow
|
|
1579
|
+
* with `setActive(name, config)` (replace can't rebuild itself — it has no
|
|
1580
|
+
* config). The sole production caller does exactly that.
|
|
1581
|
+
*/
|
|
1565
1582
|
replace(def, instance) {
|
|
1566
1583
|
this.defs.set(def.name, def);
|
|
1567
1584
|
this.instances.delete(def.name);
|
|
@@ -1654,8 +1671,12 @@ var init_modes = __esm({
|
|
|
1654
1671
|
}
|
|
1655
1672
|
replace(mode) {
|
|
1656
1673
|
this.modes.set(mode.name, mode);
|
|
1657
|
-
if (!this.active)
|
|
1674
|
+
if (!this.active) {
|
|
1658
1675
|
this.activate(mode);
|
|
1676
|
+
} else if (this.active === mode.name) {
|
|
1677
|
+
for (const fn of this.changeListeners)
|
|
1678
|
+
fn();
|
|
1679
|
+
}
|
|
1659
1680
|
}
|
|
1660
1681
|
/**
|
|
1661
1682
|
* Remove a mode. If it was active, the active slot is cleared —
|
|
@@ -1994,16 +2015,17 @@ function coerceValue(tag2, name, value, spec, errors2) {
|
|
|
1994
2015
|
}
|
|
1995
2016
|
return num;
|
|
1996
2017
|
}
|
|
2018
|
+
const decoded = decodeEntities(value);
|
|
1997
2019
|
if (spec.type === "enum") {
|
|
1998
|
-
if (!spec.values?.includes(
|
|
2020
|
+
if (!spec.values?.includes(decoded)) {
|
|
1999
2021
|
errors2.push({ message: `<${tag2}> attribute "${name}" must be one of: ${spec.values?.join(", ")}` });
|
|
2000
2022
|
}
|
|
2001
|
-
return
|
|
2023
|
+
return decoded;
|
|
2002
2024
|
}
|
|
2003
|
-
if ((name === "href" || name === "src") && !isSafeViewUrl(
|
|
2025
|
+
if ((name === "href" || name === "src") && !isSafeViewUrl(decoded, name)) {
|
|
2004
2026
|
errors2.push({ message: `<${tag2}> attribute "${name}" has a disallowed URL scheme` });
|
|
2005
2027
|
}
|
|
2006
|
-
return
|
|
2028
|
+
return decoded;
|
|
2007
2029
|
}
|
|
2008
2030
|
function coerceAttrs(b3, spec, errors2) {
|
|
2009
2031
|
const out = {};
|
|
@@ -2015,7 +2037,8 @@ function coerceAttrs(b3, spec, errors2) {
|
|
|
2015
2037
|
}
|
|
2016
2038
|
const aspec = spec?.attrs[a2.name];
|
|
2017
2039
|
if (!aspec) {
|
|
2018
|
-
|
|
2040
|
+
if (spec)
|
|
2041
|
+
errors2.push({ message: `<${b3.tag}> unknown attribute "${a2.name}"` });
|
|
2019
2042
|
continue;
|
|
2020
2043
|
}
|
|
2021
2044
|
seen.add(a2.name);
|
|
@@ -2229,10 +2252,13 @@ var init_localhost = __esm({
|
|
|
2229
2252
|
"../core/dist/tunnel/localhost.js"() {
|
|
2230
2253
|
localhostTunnel = defineTunnelProvider({
|
|
2231
2254
|
name: "localhost",
|
|
2232
|
-
open: (opts) =>
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2255
|
+
open: (opts) => {
|
|
2256
|
+
const host = opts.host.includes(":") ? `[${opts.host}]` : opts.host;
|
|
2257
|
+
return Promise.resolve({
|
|
2258
|
+
url: `http://${host}:${opts.port}`,
|
|
2259
|
+
close: () => Promise.resolve()
|
|
2260
|
+
});
|
|
2261
|
+
},
|
|
2236
2262
|
isAvailable: () => Promise.resolve(true)
|
|
2237
2263
|
});
|
|
2238
2264
|
}
|
|
@@ -3092,7 +3118,7 @@ function matchRule(rule, call, intent) {
|
|
|
3092
3118
|
if (!input || typeof input !== "object")
|
|
3093
3119
|
return false;
|
|
3094
3120
|
for (const [k3, v3] of Object.entries(rule.inputMatches)) {
|
|
3095
|
-
const candidate =
|
|
3121
|
+
const candidate = stringifyCandidate(input[k3]);
|
|
3096
3122
|
let re2;
|
|
3097
3123
|
try {
|
|
3098
3124
|
re2 = new RegExp(v3);
|
|
@@ -3108,6 +3134,18 @@ function matchRule(rule, call, intent) {
|
|
|
3108
3134
|
}
|
|
3109
3135
|
return true;
|
|
3110
3136
|
}
|
|
3137
|
+
function stringifyCandidate(value) {
|
|
3138
|
+
if (value === null || value === void 0)
|
|
3139
|
+
return "";
|
|
3140
|
+
if (typeof value === "object") {
|
|
3141
|
+
try {
|
|
3142
|
+
return JSON.stringify(value) ?? "";
|
|
3143
|
+
} catch {
|
|
3144
|
+
return String(value);
|
|
3145
|
+
}
|
|
3146
|
+
}
|
|
3147
|
+
return String(value);
|
|
3148
|
+
}
|
|
3111
3149
|
function warnBadPattern(ruleName, field, pattern, intent, err) {
|
|
3112
3150
|
const detail = err instanceof Error ? err.message : String(err);
|
|
3113
3151
|
const resolution = intent === "deny" ? "failing closed (rule still denies)" : "this field cannot match (rule will not grant)";
|
|
@@ -3760,7 +3798,16 @@ async function loadDir(dir, scope, logger) {
|
|
|
3760
3798
|
if (!entry.isFile() || !entry.name.endsWith(".md"))
|
|
3761
3799
|
continue;
|
|
3762
3800
|
const full = path3.join(dir, entry.name);
|
|
3763
|
-
|
|
3801
|
+
let raw;
|
|
3802
|
+
try {
|
|
3803
|
+
raw = await promises.readFile(full, "utf8");
|
|
3804
|
+
} catch (err) {
|
|
3805
|
+
logger?.warn("skill: unreadable file, skipping", {
|
|
3806
|
+
path: full,
|
|
3807
|
+
error: err instanceof Error ? err.message : String(err)
|
|
3808
|
+
});
|
|
3809
|
+
continue;
|
|
3810
|
+
}
|
|
3764
3811
|
const { frontmatter, body } = parseSkillFile(raw);
|
|
3765
3812
|
const parsed = skillFrontmatterSchema.safeParse(frontmatter);
|
|
3766
3813
|
if (!parsed.success) {
|
|
@@ -4211,6 +4258,17 @@ var init_persistence = __esm({
|
|
|
4211
4258
|
this.indexUpdateScheduled = false;
|
|
4212
4259
|
await this.writeIndex();
|
|
4213
4260
|
}
|
|
4261
|
+
/**
|
|
4262
|
+
* Resolve once every event-log write queued so far has settled. Appends are
|
|
4263
|
+
* enqueued fire-and-forget (`enqueueAppend`), so callers that need to observe
|
|
4264
|
+
* the on-disk result of prior appends — graceful shutdown, or a test that
|
|
4265
|
+
* mutates the filesystem between writes — await this to drain the queue
|
|
4266
|
+
* rather than guessing at timing. Enqueues a no-op at the tail of the same
|
|
4267
|
+
* mutex, so it can only resolve after all earlier appends/truncates have run.
|
|
4268
|
+
*/
|
|
4269
|
+
async settleWrites() {
|
|
4270
|
+
await this.writeQueue.run(() => void 0);
|
|
4271
|
+
}
|
|
4214
4272
|
/**
|
|
4215
4273
|
* Manually update header fields (provider/model) when the user
|
|
4216
4274
|
* switches mid-session. The /model picker calls this so the index
|
|
@@ -4233,7 +4291,7 @@ var init_persistence = __esm({
|
|
|
4233
4291
|
};
|
|
4234
4292
|
this.scheduleIndexWrite();
|
|
4235
4293
|
const line = JSON.stringify(event) + "\n";
|
|
4236
|
-
void this.writeQueue.run(() => promises.appendFile(this.logPath, line, "utf8")).then(() => this.noteWriteOk()).catch((err) => this.noteWriteFailure("append", err));
|
|
4294
|
+
void this.writeQueue.run(() => this.ensureReady().then(() => promises.appendFile(this.logPath, line, "utf8"))).then(() => this.noteWriteOk()).catch((err) => this.noteWriteFailure("append", err));
|
|
4237
4295
|
}
|
|
4238
4296
|
/**
|
|
4239
4297
|
* True while event-log writes are failing (history is no longer being
|
|
@@ -4274,7 +4332,7 @@ var init_persistence = __esm({
|
|
|
4274
4332
|
lastActivity: (/* @__PURE__ */ new Date()).toISOString()
|
|
4275
4333
|
};
|
|
4276
4334
|
this.scheduleIndexWrite();
|
|
4277
|
-
void this.writeQueue.run(() => promises.writeFile(this.logPath, "", "utf8")).then(() => this.noteWriteOk()).catch((err) => this.noteWriteFailure("truncate", err));
|
|
4335
|
+
void this.writeQueue.run(() => this.ensureReady().then(() => promises.writeFile(this.logPath, "", "utf8"))).then(() => this.noteWriteOk()).catch((err) => this.noteWriteFailure("truncate", err));
|
|
4278
4336
|
}
|
|
4279
4337
|
scheduleIndexWrite() {
|
|
4280
4338
|
if (this.indexUpdateScheduled)
|
|
@@ -30607,7 +30665,7 @@ var require_api = __commonJS({
|
|
|
30607
30665
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
30608
30666
|
exports.Api = void 0;
|
|
30609
30667
|
var client_js_1 = require_client();
|
|
30610
|
-
var
|
|
30668
|
+
var Api2 = class {
|
|
30611
30669
|
/**
|
|
30612
30670
|
* Constructs a new instance of `Api`. It is independent from all other
|
|
30613
30671
|
* instances of this class. For example, this lets you install a custom set
|
|
@@ -32942,7 +33000,7 @@ var require_api = __commonJS({
|
|
|
32942
33000
|
return this.raw.getGameHighScores({ inline_message_id, user_id }, signal);
|
|
32943
33001
|
}
|
|
32944
33002
|
};
|
|
32945
|
-
exports.Api =
|
|
33003
|
+
exports.Api = Api2;
|
|
32946
33004
|
}
|
|
32947
33005
|
});
|
|
32948
33006
|
|
|
@@ -32986,7 +33044,7 @@ var require_bot = __commonJS({
|
|
|
32986
33044
|
"chat_boost",
|
|
32987
33045
|
"removed_chat_boost"
|
|
32988
33046
|
];
|
|
32989
|
-
var
|
|
33047
|
+
var Bot2 = class extends composer_js_1.Composer {
|
|
32990
33048
|
/**
|
|
32991
33049
|
* Creates a new Bot with the given token.
|
|
32992
33050
|
*
|
|
@@ -33352,7 +33410,7 @@ var require_bot = __commonJS({
|
|
|
33352
33410
|
await sleep7(sleepSeconds);
|
|
33353
33411
|
}
|
|
33354
33412
|
};
|
|
33355
|
-
exports.Bot =
|
|
33413
|
+
exports.Bot = Bot2;
|
|
33356
33414
|
async function withRetries(task, signal) {
|
|
33357
33415
|
const INITIAL_DELAY = 50;
|
|
33358
33416
|
let lastDelay = INITIAL_DELAY;
|
|
@@ -49252,11 +49310,25 @@ function renderResult(content, isError) {
|
|
|
49252
49310
|
else if (block.type === "image")
|
|
49253
49311
|
parts.push(`[image:${block.mimeType}]`);
|
|
49254
49312
|
else if (block.type === "resource")
|
|
49255
|
-
parts.push(
|
|
49313
|
+
parts.push(renderResource(block.resource));
|
|
49256
49314
|
}
|
|
49257
49315
|
const text = parts.join("\n");
|
|
49258
49316
|
return isError ? `[error] ${text}` : text;
|
|
49259
49317
|
}
|
|
49318
|
+
function renderResource(resource) {
|
|
49319
|
+
if (resource && typeof resource === "object") {
|
|
49320
|
+
const r2 = resource;
|
|
49321
|
+
if (typeof r2.text === "string")
|
|
49322
|
+
return r2.text;
|
|
49323
|
+
const meta = [
|
|
49324
|
+
typeof r2.uri === "string" ? r2.uri : null,
|
|
49325
|
+
typeof r2.mimeType === "string" ? r2.mimeType : null
|
|
49326
|
+
].filter((v3) => v3 !== null);
|
|
49327
|
+
if (meta.length > 0)
|
|
49328
|
+
return `[resource:${meta.join(" ")}]`;
|
|
49329
|
+
}
|
|
49330
|
+
return `[resource]`;
|
|
49331
|
+
}
|
|
49260
49332
|
var MCP_CALL_TIMEOUT_MS;
|
|
49261
49333
|
var init_wrap = __esm({
|
|
49262
49334
|
"../plugin-mcp/dist/wrap.js"() {
|
|
@@ -49264,13 +49336,16 @@ var init_wrap = __esm({
|
|
|
49264
49336
|
MCP_CALL_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
49265
49337
|
}
|
|
49266
49338
|
});
|
|
49267
|
-
var mcpStoredServerSchema,
|
|
49339
|
+
var mcpStoredServerSchema, mcpStoredConfigRootSchema;
|
|
49268
49340
|
var init_config_schema = __esm({
|
|
49269
49341
|
"../plugin-mcp/dist/admin/config-schema.js"() {
|
|
49270
49342
|
mcpStoredServerSchema = z$1.object({
|
|
49271
49343
|
name: z$1.string().min(1)
|
|
49272
49344
|
}).passthrough();
|
|
49273
|
-
|
|
49345
|
+
mcpStoredConfigRootSchema = z$1.object({
|
|
49346
|
+
servers: z$1.array(z$1.unknown())
|
|
49347
|
+
}).passthrough();
|
|
49348
|
+
z$1.object({
|
|
49274
49349
|
servers: z$1.array(mcpStoredServerSchema)
|
|
49275
49350
|
}).passthrough().transform((value) => value);
|
|
49276
49351
|
}
|
|
@@ -49281,10 +49356,17 @@ function mcpConfigPath() {
|
|
|
49281
49356
|
async function readMcpConfig() {
|
|
49282
49357
|
try {
|
|
49283
49358
|
const raw = await promises.readFile(mcpConfigPath(), "utf8");
|
|
49284
|
-
const
|
|
49285
|
-
if (
|
|
49286
|
-
return
|
|
49359
|
+
const root = mcpStoredConfigRootSchema.safeParse(JSON.parse(raw));
|
|
49360
|
+
if (!root.success) {
|
|
49361
|
+
return { servers: [] };
|
|
49287
49362
|
}
|
|
49363
|
+
const servers = [];
|
|
49364
|
+
for (const entry of root.data.servers) {
|
|
49365
|
+
const parsed = mcpStoredServerSchema.safeParse(entry);
|
|
49366
|
+
if (parsed.success)
|
|
49367
|
+
servers.push(parsed.data);
|
|
49368
|
+
}
|
|
49369
|
+
return { servers };
|
|
49288
49370
|
} catch {
|
|
49289
49371
|
}
|
|
49290
49372
|
return { servers: [] };
|
|
@@ -49917,23 +49999,19 @@ __export(dist_exports2, {
|
|
|
49917
49999
|
});
|
|
49918
50000
|
async function createMcpPlugin(opts) {
|
|
49919
50001
|
const factory2 = opts.clientFactory ?? defaultClientFactory;
|
|
49920
|
-
const
|
|
49921
|
-
const
|
|
49922
|
-
|
|
49923
|
-
|
|
49924
|
-
|
|
49925
|
-
|
|
49926
|
-
|
|
49927
|
-
|
|
49928
|
-
|
|
49929
|
-
|
|
49930
|
-
});
|
|
49931
|
-
tools.push(...wrapped);
|
|
49932
|
-
}
|
|
49933
|
-
} catch (err) {
|
|
49934
|
-
await Promise.allSettled(clients.map((c2) => c2.close()));
|
|
49935
|
-
throw err;
|
|
50002
|
+
const opened = [];
|
|
50003
|
+
const results = await Promise.allSettled(opts.servers.map(async (server) => {
|
|
50004
|
+
const client = await factory2(server, opts);
|
|
50005
|
+
opened.push(client);
|
|
50006
|
+
return wrapMcpServerTools({ server, client, toolNamePrefix: opts.toolNamePrefix });
|
|
50007
|
+
}));
|
|
50008
|
+
const failure = results.find((r2) => r2.status === "rejected");
|
|
50009
|
+
if (failure) {
|
|
50010
|
+
await Promise.allSettled(opened.map((c2) => c2.close()));
|
|
50011
|
+
throw failure.reason;
|
|
49936
50012
|
}
|
|
50013
|
+
const tools = results.flatMap((r2) => r2.value);
|
|
50014
|
+
const clients = opened;
|
|
49937
50015
|
return definePlugin({
|
|
49938
50016
|
name: "@moxxy/plugin-mcp",
|
|
49939
50017
|
version: "0.0.0",
|
|
@@ -51297,7 +51375,7 @@ var require_react_development = __commonJS({
|
|
|
51297
51375
|
var dispatcher = resolveDispatcher();
|
|
51298
51376
|
return dispatcher.useRef(initialValue);
|
|
51299
51377
|
}
|
|
51300
|
-
function
|
|
51378
|
+
function useEffect18(create2, deps) {
|
|
51301
51379
|
var dispatcher = resolveDispatcher();
|
|
51302
51380
|
return dispatcher.useEffect(create2, deps);
|
|
51303
51381
|
}
|
|
@@ -52080,7 +52158,7 @@ var require_react_development = __commonJS({
|
|
|
52080
52158
|
exports.useContext = useContext7;
|
|
52081
52159
|
exports.useDebugValue = useDebugValue;
|
|
52082
52160
|
exports.useDeferredValue = useDeferredValue;
|
|
52083
|
-
exports.useEffect =
|
|
52161
|
+
exports.useEffect = useEffect18;
|
|
52084
52162
|
exports.useId = useId;
|
|
52085
52163
|
exports.useImperativeHandle = useImperativeHandle;
|
|
52086
52164
|
exports.useInsertionEffect = useInsertionEffect;
|
|
@@ -84849,7 +84927,11 @@ function parseInputChunk(chunk, ctx) {
|
|
|
84849
84927
|
continue;
|
|
84850
84928
|
}
|
|
84851
84929
|
if (c2 === "") {
|
|
84852
|
-
|
|
84930
|
+
if (ctx.onInterrupt)
|
|
84931
|
+
ctx.onInterrupt();
|
|
84932
|
+
else
|
|
84933
|
+
process.exit(0);
|
|
84934
|
+
return remainder;
|
|
84853
84935
|
}
|
|
84854
84936
|
if (c2 === "\x7F" || c2 === "\b") {
|
|
84855
84937
|
ctx.dispatch({ type: "delete-back" });
|
|
@@ -84973,7 +85055,7 @@ var init_PromptInput = __esm({
|
|
|
84973
85055
|
init_external_insert();
|
|
84974
85056
|
init_reducer();
|
|
84975
85057
|
init_parse_input();
|
|
84976
|
-
PromptInput = ({ onSubmit, disabled, placeholder, slashCommands = BUILTIN_SLASH_COMMANDS, onPasteText, commandHotkeys, onShiftTab, externalInsert }) => {
|
|
85058
|
+
PromptInput = ({ onSubmit, disabled, placeholder, slashCommands = BUILTIN_SLASH_COMMANDS, onPasteText, commandHotkeys, onShiftTab, onInterrupt, externalInsert }) => {
|
|
84977
85059
|
const [state, dispatch3] = (0, import_react26.useReducer)(reducer, INITIAL);
|
|
84978
85060
|
const [slashCursor, setSlashCursor] = import_react26.default.useState(0);
|
|
84979
85061
|
const stateRef = (0, import_react26.useRef)(state);
|
|
@@ -85033,6 +85115,8 @@ var init_PromptInput = __esm({
|
|
|
85033
85115
|
commandHotkeysRef.current = commandHotkeys;
|
|
85034
85116
|
const onShiftTabRef = (0, import_react26.useRef)(onShiftTab);
|
|
85035
85117
|
onShiftTabRef.current = onShiftTab;
|
|
85118
|
+
const onInterruptRef = (0, import_react26.useRef)(onInterrupt);
|
|
85119
|
+
onInterruptRef.current = onInterrupt;
|
|
85036
85120
|
const lastExternalInsertIdRef = (0, import_react26.useRef)(null);
|
|
85037
85121
|
(0, import_react26.useEffect)(() => {
|
|
85038
85122
|
const decision = nextExternalInsertAction(lastExternalInsertIdRef.current, externalInsert);
|
|
@@ -85058,6 +85142,13 @@ var init_PromptInput = __esm({
|
|
|
85058
85142
|
onSlashDown: () => setSlashCursor((c2) => Math.min(slashMatchesRef.current.length - 1, c2 + 1)),
|
|
85059
85143
|
onSlashAccept: handleSlashAccept,
|
|
85060
85144
|
onShiftTab: () => onShiftTabRef.current?.(),
|
|
85145
|
+
onInterrupt: () => {
|
|
85146
|
+
const fn = onInterruptRef.current;
|
|
85147
|
+
if (fn)
|
|
85148
|
+
fn();
|
|
85149
|
+
else
|
|
85150
|
+
process.exit(0);
|
|
85151
|
+
},
|
|
85061
85152
|
onPasteText: (text) => onPasteTextRef.current?.(text) ?? text,
|
|
85062
85153
|
slashOpen: false,
|
|
85063
85154
|
bufferRef: { current: { buffer: "", cursor: 0 } },
|
|
@@ -85415,12 +85506,9 @@ function handleCollabEvent(e3, ref, root) {
|
|
|
85415
85506
|
root.push(block2);
|
|
85416
85507
|
return;
|
|
85417
85508
|
}
|
|
85418
|
-
|
|
85419
|
-
if (!block)
|
|
85420
|
-
|
|
85421
|
-
ref.current = block;
|
|
85422
|
-
root.push(block);
|
|
85423
|
-
}
|
|
85509
|
+
const block = ref.current;
|
|
85510
|
+
if (!block)
|
|
85511
|
+
return;
|
|
85424
85512
|
switch (e3.subtype) {
|
|
85425
85513
|
case "collab_fallback_sequential":
|
|
85426
85514
|
block.fallbackReason = String(p3.reason ?? "");
|
|
@@ -87122,8 +87210,37 @@ var init_image_attachments = __esm({
|
|
|
87122
87210
|
};
|
|
87123
87211
|
}
|
|
87124
87212
|
});
|
|
87213
|
+
function reapStale(dir, now = Date.now()) {
|
|
87214
|
+
let entries;
|
|
87215
|
+
try {
|
|
87216
|
+
entries = readdirSync(dir);
|
|
87217
|
+
} catch {
|
|
87218
|
+
return;
|
|
87219
|
+
}
|
|
87220
|
+
for (const name of entries) {
|
|
87221
|
+
const m3 = CLIP_NAME_RE.exec(name);
|
|
87222
|
+
if (!m3)
|
|
87223
|
+
continue;
|
|
87224
|
+
const stamp = Number(m3[1]);
|
|
87225
|
+
let age = Number.isFinite(stamp) ? now - stamp : Number.NaN;
|
|
87226
|
+
if (!Number.isFinite(age)) {
|
|
87227
|
+
try {
|
|
87228
|
+
age = now - statSync(path3__default.join(dir, name)).mtimeMs;
|
|
87229
|
+
} catch {
|
|
87230
|
+
continue;
|
|
87231
|
+
}
|
|
87232
|
+
}
|
|
87233
|
+
if (age > CACHE_TTL_MS) {
|
|
87234
|
+
try {
|
|
87235
|
+
unlinkSync(path3__default.join(dir, name));
|
|
87236
|
+
} catch {
|
|
87237
|
+
}
|
|
87238
|
+
}
|
|
87239
|
+
}
|
|
87240
|
+
}
|
|
87125
87241
|
function ensureCacheDir() {
|
|
87126
87242
|
mkdirSync(CACHE_DIR, { recursive: true });
|
|
87243
|
+
reapStale(CACHE_DIR, Date.now());
|
|
87127
87244
|
return CACHE_DIR;
|
|
87128
87245
|
}
|
|
87129
87246
|
function nextCachePath() {
|
|
@@ -87208,10 +87325,12 @@ function readClipboardImageSync() {
|
|
|
87208
87325
|
return readClipboardImageLinux();
|
|
87209
87326
|
return null;
|
|
87210
87327
|
}
|
|
87211
|
-
var CACHE_DIR;
|
|
87328
|
+
var CACHE_DIR, CACHE_TTL_MS, CLIP_NAME_RE;
|
|
87212
87329
|
var init_clipboard_image = __esm({
|
|
87213
87330
|
"../plugin-cli/dist/clipboard-image.js"() {
|
|
87214
87331
|
CACHE_DIR = moxxyPath("image-cache");
|
|
87332
|
+
CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
87333
|
+
CLIP_NAME_RE = /^clip-(\d+)-[a-z0-9]+\.png$/;
|
|
87215
87334
|
}
|
|
87216
87335
|
});
|
|
87217
87336
|
|
|
@@ -87641,7 +87760,7 @@ function useVoiceInput(opts) {
|
|
|
87641
87760
|
const pcm = await recording.stop();
|
|
87642
87761
|
const transcriber = resolveCodexTranscriber(session);
|
|
87643
87762
|
const result = await transcriber.transcribe(pcm, {
|
|
87644
|
-
mimeType:
|
|
87763
|
+
mimeType: MOXXY_PCM16_24KHZ_MIME
|
|
87645
87764
|
});
|
|
87646
87765
|
const text = result.text.trim();
|
|
87647
87766
|
if (!text) {
|
|
@@ -87748,13 +87867,12 @@ function formatVoiceError(err) {
|
|
|
87748
87867
|
return `voice: ${message}`;
|
|
87749
87868
|
return `voice: ${message || "failed"}`;
|
|
87750
87869
|
}
|
|
87751
|
-
var import_react51, CODEX_TRANSCRIBER_NAME
|
|
87870
|
+
var import_react51, CODEX_TRANSCRIBER_NAME;
|
|
87752
87871
|
var init_use_voice_input = __esm({
|
|
87753
87872
|
"../plugin-cli/dist/session/use-voice-input.js"() {
|
|
87754
87873
|
import_react51 = __toESM(require_react());
|
|
87755
87874
|
init_voice_input();
|
|
87756
87875
|
CODEX_TRANSCRIBER_NAME = "openai-codex-transcribe";
|
|
87757
|
-
MOXXY_PCM16_24KHZ_MIME2 = "audio/x-moxxy-pcm16-24khz";
|
|
87758
87876
|
}
|
|
87759
87877
|
});
|
|
87760
87878
|
|
|
@@ -88044,13 +88162,17 @@ function startCollab(deps, arg) {
|
|
|
88044
88162
|
deps.submitPrompt(objective);
|
|
88045
88163
|
}
|
|
88046
88164
|
function openModePicker(deps, arg = "") {
|
|
88047
|
-
const modes = deps.session.modes.list();
|
|
88165
|
+
const modes = deps.session.modes.list().filter((m3) => !COLLAB_HIDDEN_MODES.has(m3.name));
|
|
88048
88166
|
if (modes.length === 0) {
|
|
88049
88167
|
deps.setSystemNotice("no modes registered");
|
|
88050
88168
|
return;
|
|
88051
88169
|
}
|
|
88052
88170
|
const target = arg.trim().toLowerCase();
|
|
88053
88171
|
if (target) {
|
|
88172
|
+
if (COLLAB_HIDDEN_MODES.has(target)) {
|
|
88173
|
+
deps.setSystemNotice("Use /collab <goal> to run a collaborative team (only one runs at a time).");
|
|
88174
|
+
return;
|
|
88175
|
+
}
|
|
88054
88176
|
const match = modes.find((m3) => m3.name.toLowerCase() === target);
|
|
88055
88177
|
if (match) {
|
|
88056
88178
|
try {
|
|
@@ -88076,7 +88198,7 @@ function openModePicker(deps, arg = "") {
|
|
|
88076
88198
|
function truncate5(s2, n2) {
|
|
88077
88199
|
return s2.length <= n2 ? s2 : s2.slice(0, n2 - 1) + "\u2026";
|
|
88078
88200
|
}
|
|
88079
|
-
var PLUGIN_KIND_TAB, OTHER_TAB, PLUGIN_TAB_ORDER;
|
|
88201
|
+
var PLUGIN_KIND_TAB, OTHER_TAB, PLUGIN_TAB_ORDER, COLLAB_HIDDEN_MODES;
|
|
88080
88202
|
var init_run_slash = __esm({
|
|
88081
88203
|
async "../plugin-cli/dist/session/run-slash.js"() {
|
|
88082
88204
|
init_dist();
|
|
@@ -88091,6 +88213,11 @@ var init_run_slash = __esm({
|
|
|
88091
88213
|
};
|
|
88092
88214
|
OTHER_TAB = { id: "others", label: "Others" };
|
|
88093
88215
|
PLUGIN_TAB_ORDER = ["providers", "modes", "channels", "tools", "others"];
|
|
88216
|
+
COLLAB_HIDDEN_MODES = /* @__PURE__ */ new Set([
|
|
88217
|
+
"collaborative",
|
|
88218
|
+
"collab-architect",
|
|
88219
|
+
"collab-peer"
|
|
88220
|
+
]);
|
|
88094
88221
|
}
|
|
88095
88222
|
});
|
|
88096
88223
|
|
|
@@ -89063,11 +89190,11 @@ var init_OverlayOrNotice = __esm({
|
|
|
89063
89190
|
});
|
|
89064
89191
|
|
|
89065
89192
|
// ../plugin-cli/dist/components/PermissionDialog.js
|
|
89066
|
-
var import_jsx_runtime32,
|
|
89193
|
+
var import_jsx_runtime32, PermissionDialog;
|
|
89067
89194
|
var init_PermissionDialog = __esm({
|
|
89068
89195
|
async "../plugin-cli/dist/components/PermissionDialog.js"() {
|
|
89069
89196
|
import_jsx_runtime32 = __toESM(require_jsx_runtime());
|
|
89070
|
-
|
|
89197
|
+
__toESM(require_react());
|
|
89071
89198
|
await init_build2();
|
|
89072
89199
|
init_theme();
|
|
89073
89200
|
await init_Modal();
|
|
@@ -89083,8 +89210,6 @@ var init_PermissionDialog = __esm({
|
|
|
89083
89210
|
else if (ch === "n" || key.escape)
|
|
89084
89211
|
onDecide({ mode: "deny", reason: "user declined" });
|
|
89085
89212
|
});
|
|
89086
|
-
(0, import_react60.useEffect)(() => {
|
|
89087
|
-
}, []);
|
|
89088
89213
|
const title = queueDepth > 0 ? `Tool permission requested (${queueDepth} more queued)` : "Tool permission requested";
|
|
89089
89214
|
return (0, import_jsx_runtime32.jsxs)(Modal, { title, hints: "y allow \xB7 a session \xB7 p always \xB7 n deny", children: [(0, import_jsx_runtime32.jsxs)(Text, { children: ["Tool: ", (0, import_jsx_runtime32.jsx)(Text, { bold: true, children: call.name }), toolDescription ? (0, import_jsx_runtime32.jsxs)(Text, { dimColor: true, children: [" \u2014 ", toolDescription] }) : null] }), (0, import_jsx_runtime32.jsxs)(Text, { dimColor: true, children: ["Input: ", JSON.stringify(call.input).slice(0, 200)] }), (0, import_jsx_runtime32.jsxs)(Text, { children: [(0, import_jsx_runtime32.jsx)(Text, { children: "[y]" }), (0, import_jsx_runtime32.jsx)(Text, { dimColor: true, children: " allow once \xB7 " }), (0, import_jsx_runtime32.jsx)(Text, { children: "[a]" }), (0, import_jsx_runtime32.jsx)(Text, { dimColor: true, children: " allow session \xB7 " }), (0, import_jsx_runtime32.jsx)(Text, { children: "[p]" }), (0, import_jsx_runtime32.jsx)(Text, { dimColor: true, children: " always \xB7 " }), (0, import_jsx_runtime32.jsx)(Text, { color: Colors.danger, children: "[n]" }), (0, import_jsx_runtime32.jsx)(Text, { dimColor: true, children: " deny" })] })] });
|
|
89090
89215
|
};
|
|
@@ -89092,6 +89217,11 @@ var init_PermissionDialog = __esm({
|
|
|
89092
89217
|
});
|
|
89093
89218
|
|
|
89094
89219
|
// ../plugin-cli/dist/components/ApprovalDialog.js
|
|
89220
|
+
function jkScrolls(letter, renderedLineCount, options) {
|
|
89221
|
+
if (renderedLineCount <= MAX_BODY_LINES)
|
|
89222
|
+
return false;
|
|
89223
|
+
return !options.some((o2) => o2.hotkey === letter);
|
|
89224
|
+
}
|
|
89095
89225
|
function isDiffBody(body) {
|
|
89096
89226
|
return /```diff\b/.test(body) || /^\s*diff --git\b/m.test(body);
|
|
89097
89227
|
}
|
|
@@ -89148,6 +89278,10 @@ var init_ApprovalDialog = __esm({
|
|
|
89148
89278
|
const [cursor, setCursor] = (0, import_react61.useState)(initialCursor);
|
|
89149
89279
|
const [textEntry, setTextEntry] = (0, import_react61.useState)(null);
|
|
89150
89280
|
const [scrollOffset, setScrollOffset] = (0, import_react61.useState)(0);
|
|
89281
|
+
const rawLines = request.body.split("\n");
|
|
89282
|
+
const diffMode = isDiffBody(request.body);
|
|
89283
|
+
const rendered = diffMode ? renderDiffLines(rawLines) : rawLines.map((text) => ({ text }));
|
|
89284
|
+
const scrollClaims = (letter) => jkScrolls(letter, rendered.length, request.options);
|
|
89151
89285
|
use_input_default((input, key) => {
|
|
89152
89286
|
if (textEntry) {
|
|
89153
89287
|
if (key.return) {
|
|
@@ -89171,11 +89305,12 @@ var init_ApprovalDialog = __esm({
|
|
|
89171
89305
|
}
|
|
89172
89306
|
return;
|
|
89173
89307
|
}
|
|
89174
|
-
|
|
89175
|
-
|
|
89308
|
+
const maxScroll = Math.max(0, rendered.length - MAX_BODY_LINES);
|
|
89309
|
+
if (input === "j" && scrollClaims("j") || key.pageDown) {
|
|
89310
|
+
setScrollOffset((o2) => Math.min(maxScroll, o2 + Math.max(1, Math.floor(MAX_BODY_LINES / 2))));
|
|
89176
89311
|
return;
|
|
89177
89312
|
}
|
|
89178
|
-
if (input === "k" || key.pageUp) {
|
|
89313
|
+
if (input === "k" && scrollClaims("k") || key.pageUp) {
|
|
89179
89314
|
setScrollOffset((o2) => Math.max(0, o2 - Math.max(1, Math.floor(MAX_BODY_LINES / 2))));
|
|
89180
89315
|
return;
|
|
89181
89316
|
}
|
|
@@ -89223,9 +89358,6 @@ var init_ApprovalDialog = __esm({
|
|
|
89223
89358
|
}
|
|
89224
89359
|
onDecide({ optionId: id });
|
|
89225
89360
|
};
|
|
89226
|
-
const rawLines = request.body.split("\n");
|
|
89227
|
-
const diffMode = isDiffBody(request.body);
|
|
89228
|
-
const rendered = diffMode ? renderDiffLines(rawLines) : rawLines.map((text) => ({ text }));
|
|
89229
89361
|
const maxOffset = Math.max(0, rendered.length - MAX_BODY_LINES);
|
|
89230
89362
|
const offset = Math.min(scrollOffset, maxOffset);
|
|
89231
89363
|
const visible = rendered.slice(offset, offset + MAX_BODY_LINES);
|
|
@@ -113398,7 +113530,7 @@ async function provisionWorkspace(opts) {
|
|
|
113398
113530
|
if (install.code !== 0) {
|
|
113399
113531
|
const loose = await run("pnpm", ["install"], dir);
|
|
113400
113532
|
if (loose.code !== 0)
|
|
113401
|
-
return { ok: false, message: `pnpm install failed: ${trunc(
|
|
113533
|
+
return { ok: false, message: `pnpm install failed: ${trunc(loose.output, 400)}`, repoDir: dir };
|
|
113402
113534
|
}
|
|
113403
113535
|
return { ok: true, message: "workspace provisioned", repoDir: dir };
|
|
113404
113536
|
}
|
|
@@ -114031,6 +114163,23 @@ async function escalate(deps, ctx, journal, reason) {
|
|
|
114031
114163
|
}
|
|
114032
114164
|
|
|
114033
114165
|
// src/argv.ts
|
|
114166
|
+
var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
|
|
114167
|
+
"help",
|
|
114168
|
+
"h",
|
|
114169
|
+
"version",
|
|
114170
|
+
"v",
|
|
114171
|
+
"yes",
|
|
114172
|
+
"y",
|
|
114173
|
+
"verbose",
|
|
114174
|
+
"allow-all",
|
|
114175
|
+
"standalone",
|
|
114176
|
+
"attach",
|
|
114177
|
+
"reload",
|
|
114178
|
+
"all",
|
|
114179
|
+
"stop",
|
|
114180
|
+
"status",
|
|
114181
|
+
"background"
|
|
114182
|
+
]);
|
|
114034
114183
|
function parseArgv(argv) {
|
|
114035
114184
|
const result = { command: "", flags: {}, positional: [] };
|
|
114036
114185
|
if (argv.length === 0) {
|
|
@@ -114053,7 +114202,7 @@ function parseArgv(argv) {
|
|
|
114053
114202
|
} else {
|
|
114054
114203
|
const key = a2.slice(2);
|
|
114055
114204
|
const next = argv[i2 + 1];
|
|
114056
|
-
if (next && !next.startsWith("-")) {
|
|
114205
|
+
if (next && !next.startsWith("-") && !BOOLEAN_FLAGS.has(key)) {
|
|
114057
114206
|
result.flags[key] = next;
|
|
114058
114207
|
i2++;
|
|
114059
114208
|
} else {
|
|
@@ -114063,7 +114212,7 @@ function parseArgv(argv) {
|
|
|
114063
114212
|
} else if (a2.startsWith("-")) {
|
|
114064
114213
|
const key = a2.slice(1);
|
|
114065
114214
|
const next = argv[i2 + 1];
|
|
114066
|
-
if (next && !next.startsWith("-")) {
|
|
114215
|
+
if (next && !next.startsWith("-") && !BOOLEAN_FLAGS.has(key)) {
|
|
114067
114216
|
result.flags[key] = next;
|
|
114068
114217
|
i2++;
|
|
114069
114218
|
} else {
|
|
@@ -114317,17 +114466,19 @@ async function loadOne(filePath) {
|
|
|
114317
114466
|
}
|
|
114318
114467
|
return parsed.data;
|
|
114319
114468
|
}
|
|
114320
|
-
var cachedJiti =
|
|
114469
|
+
var cachedJiti = /* @__PURE__ */ new Map();
|
|
114321
114470
|
async function getJiti2(cwd2) {
|
|
114322
|
-
|
|
114323
|
-
|
|
114471
|
+
const existing = cachedJiti.get(cwd2);
|
|
114472
|
+
if (existing)
|
|
114473
|
+
return existing;
|
|
114324
114474
|
try {
|
|
114325
114475
|
const mod = await Promise.resolve().then(() => (init_jiti(), jiti_exports));
|
|
114326
114476
|
const factory2 = mod.createJiti ?? mod.default;
|
|
114327
114477
|
if (!factory2)
|
|
114328
114478
|
return null;
|
|
114329
|
-
|
|
114330
|
-
|
|
114479
|
+
const instance = factory2(cwd2, { interopDefault: true });
|
|
114480
|
+
cachedJiti.set(cwd2, instance);
|
|
114481
|
+
return instance;
|
|
114331
114482
|
} catch {
|
|
114332
114483
|
return null;
|
|
114333
114484
|
}
|
|
@@ -114586,9 +114737,22 @@ function decrypt(blob, key) {
|
|
|
114586
114737
|
return plaintext.toString("utf8");
|
|
114587
114738
|
}
|
|
114588
114739
|
function randomCode(digits = 6) {
|
|
114589
|
-
|
|
114590
|
-
|
|
114591
|
-
|
|
114740
|
+
if (!Number.isInteger(digits) || digits < 1) {
|
|
114741
|
+
throw new Error(`randomCode: digits must be a positive integer, got ${digits}`);
|
|
114742
|
+
}
|
|
114743
|
+
const modulus = 10n ** BigInt(digits);
|
|
114744
|
+
const byteLen = Math.ceil(digits * Math.log2(10) / 8) + 1;
|
|
114745
|
+
const space = 1n << BigInt(byteLen * 8);
|
|
114746
|
+
const limit2 = space - space % modulus;
|
|
114747
|
+
for (; ; ) {
|
|
114748
|
+
let value = 0n;
|
|
114749
|
+
for (const byte of randomBytes(byteLen)) {
|
|
114750
|
+
value = value << 8n | BigInt(byte);
|
|
114751
|
+
}
|
|
114752
|
+
if (value < limit2) {
|
|
114753
|
+
return (value % modulus).toString().padStart(digits, "0");
|
|
114754
|
+
}
|
|
114755
|
+
}
|
|
114592
114756
|
}
|
|
114593
114757
|
|
|
114594
114758
|
// ../plugin-vault/dist/keysource.js
|
|
@@ -114609,10 +114773,10 @@ function createCombinedKeySource(opts) {
|
|
|
114609
114773
|
},
|
|
114610
114774
|
async obtain(salt) {
|
|
114611
114775
|
const envName = opts.envVar ?? "MOXXY_VAULT_PASSPHRASE";
|
|
114612
|
-
const
|
|
114613
|
-
if (
|
|
114776
|
+
const envValue2 = process.env[envName];
|
|
114777
|
+
if (envValue2) {
|
|
114614
114778
|
resolvedName = `env:${envName}`;
|
|
114615
|
-
return deriveKey(
|
|
114779
|
+
return deriveKey(envValue2, salt);
|
|
114616
114780
|
}
|
|
114617
114781
|
if (!opts.disableKeytar) {
|
|
114618
114782
|
const fromKeychain = await tryKeychainGet();
|
|
@@ -115404,7 +115568,7 @@ async function safeRead(filePath) {
|
|
|
115404
115568
|
const result = memoryFrontmatterSchema.safeParse(parsed.frontmatter);
|
|
115405
115569
|
if (!result.success)
|
|
115406
115570
|
return null;
|
|
115407
|
-
return { frontmatter: result.data, body: parsed.body };
|
|
115571
|
+
return { frontmatter: result.data, body: parsed.body.trim() };
|
|
115408
115572
|
} catch {
|
|
115409
115573
|
return null;
|
|
115410
115574
|
}
|
|
@@ -117036,6 +117200,14 @@ async function syncSkillSchedules(registry, store) {
|
|
|
117036
117200
|
}
|
|
117037
117201
|
|
|
117038
117202
|
// ../plugin-scheduler/dist/poller.js
|
|
117203
|
+
function cronBaseline(entry) {
|
|
117204
|
+
return entry.lastRunAt ?? entry.createdAt;
|
|
117205
|
+
}
|
|
117206
|
+
function nextCronFire(entry) {
|
|
117207
|
+
if (!entry.cron)
|
|
117208
|
+
return null;
|
|
117209
|
+
return nextFireTime(entry.cron, new Date(cronBaseline(entry)), entry.timeZone);
|
|
117210
|
+
}
|
|
117039
117211
|
function isDue(entry, now) {
|
|
117040
117212
|
if (!entry.enabled)
|
|
117041
117213
|
return false;
|
|
@@ -117044,8 +117216,7 @@ function isDue(entry, now) {
|
|
|
117044
117216
|
}
|
|
117045
117217
|
if (!entry.cron)
|
|
117046
117218
|
return false;
|
|
117047
|
-
const
|
|
117048
|
-
const next = nextFireTime(entry.cron, new Date(since), entry.timeZone);
|
|
117219
|
+
const next = nextCronFire(entry);
|
|
117049
117220
|
if (!next)
|
|
117050
117221
|
return false;
|
|
117051
117222
|
return next.getTime() <= now;
|
|
@@ -117079,23 +117250,12 @@ var SchedulerPoller = class {
|
|
|
117079
117250
|
await this.tickPromise.catch(() => void 0);
|
|
117080
117251
|
}
|
|
117081
117252
|
/** Fire any due schedules right now, ignoring the timer cadence.
|
|
117082
|
-
* Returns the number of schedules that
|
|
117253
|
+
* Returns the number of due schedules that were attempted — counted at the
|
|
117254
|
+
* attempt point, so a schedule that fired but failed mid-run (e.g. a
|
|
117255
|
+
* store.update throw) is still counted, unlike piggy-backing on onFired
|
|
117256
|
+
* (which only fires on a clean run). */
|
|
117083
117257
|
async tickOnce() {
|
|
117084
|
-
|
|
117085
|
-
const original = this.opts.onFired;
|
|
117086
|
-
let wrapped;
|
|
117087
|
-
if (original) {
|
|
117088
|
-
wrapped = (entry, outcome) => {
|
|
117089
|
-
count += 1;
|
|
117090
|
-
original(entry, outcome);
|
|
117091
|
-
};
|
|
117092
|
-
} else {
|
|
117093
|
-
wrapped = () => {
|
|
117094
|
-
count += 1;
|
|
117095
|
-
};
|
|
117096
|
-
}
|
|
117097
|
-
await this.tickWith(wrapped);
|
|
117098
|
-
return count;
|
|
117258
|
+
return this.tickWith(this.opts.onFired);
|
|
117099
117259
|
}
|
|
117100
117260
|
async tick() {
|
|
117101
117261
|
if (!this.running)
|
|
@@ -117120,11 +117280,13 @@ var SchedulerPoller = class {
|
|
|
117120
117280
|
this.opts.logger?.error?.("scheduler: failed to read store", {
|
|
117121
117281
|
err: err instanceof Error ? err.message : String(err)
|
|
117122
117282
|
});
|
|
117123
|
-
return;
|
|
117283
|
+
return 0;
|
|
117124
117284
|
}
|
|
117285
|
+
let attempted = 0;
|
|
117125
117286
|
for (const entry of schedules) {
|
|
117126
117287
|
if (!isDue(entry, now))
|
|
117127
117288
|
continue;
|
|
117289
|
+
attempted += 1;
|
|
117128
117290
|
try {
|
|
117129
117291
|
const outcome = await runSchedule(entry, this.opts.runner, this.opts.store, this.opts.inbox);
|
|
117130
117292
|
this.opts.logger?.info?.("scheduler: fired", {
|
|
@@ -117140,6 +117302,7 @@ var SchedulerPoller = class {
|
|
|
117140
117302
|
});
|
|
117141
117303
|
}
|
|
117142
117304
|
}
|
|
117305
|
+
return attempted;
|
|
117143
117306
|
}
|
|
117144
117307
|
};
|
|
117145
117308
|
|
|
@@ -117382,11 +117545,9 @@ var cronOrTimestamp = z$1.object({
|
|
|
117382
117545
|
message: "provide either `cron` or `runAt`"
|
|
117383
117546
|
});
|
|
117384
117547
|
function describeEntry(entry) {
|
|
117385
|
-
const now = Date.now();
|
|
117386
117548
|
let nextFireAt = null;
|
|
117387
117549
|
if (entry.cron) {
|
|
117388
|
-
const
|
|
117389
|
-
const next = nextFireTime(entry.cron, new Date(since), entry.timeZone);
|
|
117550
|
+
const next = nextCronFire(entry);
|
|
117390
117551
|
nextFireAt = next ? next.getTime() : null;
|
|
117391
117552
|
} else if (entry.runAt && entry.enabled) {
|
|
117392
117553
|
nextFireAt = entry.runAt;
|
|
@@ -117680,7 +117841,8 @@ function defaultWebhookInboxDir() {
|
|
|
117680
117841
|
async function writeInbox2(trigger, result, deliveryId, opts = {}) {
|
|
117681
117842
|
const dir = opts.dir ?? defaultWebhookInboxDir();
|
|
117682
117843
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
117683
|
-
const
|
|
117844
|
+
const suffix = deliveryId ? deliveryId.replace(/[^A-Za-z0-9._-]/g, "_") : randomUUID().slice(0, 8);
|
|
117845
|
+
const file = path3__default.join(dir, `${stamp}-${trigger.name}-${suffix}.md`);
|
|
117684
117846
|
const header = [
|
|
117685
117847
|
"---",
|
|
117686
117848
|
`webhook: ${trigger.name}`,
|
|
@@ -118047,14 +118209,6 @@ var WebhookServer = class {
|
|
|
118047
118209
|
res.end(JSON.stringify({ error: "verification_failed" }));
|
|
118048
118210
|
return;
|
|
118049
118211
|
}
|
|
118050
|
-
if (!shouldFire(trigger.filters, { headers: req.headers, body })) {
|
|
118051
|
-
this.opts.logger?.info?.("webhooks: filtered out, not firing", {
|
|
118052
|
-
trigger: trigger.name
|
|
118053
|
-
});
|
|
118054
|
-
res.writeHead(200, { "content-type": "application/json" });
|
|
118055
|
-
res.end(JSON.stringify({ status: "filtered" }));
|
|
118056
|
-
return;
|
|
118057
|
-
}
|
|
118058
118212
|
const idempKey = idempotencyKey(trigger, req.headers);
|
|
118059
118213
|
if (idempKey && !this.dedupe.check(trigger.id, idempKey)) {
|
|
118060
118214
|
this.opts.logger?.info?.("webhooks: duplicate delivery, dropped", {
|
|
@@ -118065,6 +118219,14 @@ var WebhookServer = class {
|
|
|
118065
118219
|
res.end(JSON.stringify({ status: "duplicate" }));
|
|
118066
118220
|
return;
|
|
118067
118221
|
}
|
|
118222
|
+
if (!shouldFire(trigger.filters, { headers: req.headers, body })) {
|
|
118223
|
+
this.opts.logger?.info?.("webhooks: filtered out, not firing", {
|
|
118224
|
+
trigger: trigger.name
|
|
118225
|
+
});
|
|
118226
|
+
res.writeHead(200, { "content-type": "application/json" });
|
|
118227
|
+
res.end(JSON.stringify({ status: "filtered" }));
|
|
118228
|
+
return;
|
|
118229
|
+
}
|
|
118068
118230
|
res.writeHead(202, { "content-type": "application/json" });
|
|
118069
118231
|
res.end(JSON.stringify({ status: "accepted", trigger: trigger.name }));
|
|
118070
118232
|
const prompt = renderPrompt({
|
|
@@ -119138,17 +119300,28 @@ function isPathKey(key) {
|
|
|
119138
119300
|
function isUrlKey(key) {
|
|
119139
119301
|
return tokenize4(key).some((w4) => URL_WORDS.has(w4));
|
|
119140
119302
|
}
|
|
119303
|
+
var INVALID_FILE_URL_PATH = "/\0moxxy-invalid-file-url";
|
|
119141
119304
|
function extractPaths(input) {
|
|
119142
119305
|
const out = [];
|
|
119143
119306
|
walkStrings(input, (key, value) => {
|
|
119144
|
-
if (
|
|
119307
|
+
if (value.startsWith("file://")) {
|
|
119308
|
+
out.push(fileUrlPath(value));
|
|
119309
|
+
} else if (isPathKey(key)) {
|
|
119145
119310
|
out.push(value);
|
|
119146
|
-
} else if (value.startsWith("file://")) {
|
|
119147
|
-
out.push(value.slice("file://".length));
|
|
119148
119311
|
}
|
|
119149
119312
|
});
|
|
119150
119313
|
return out;
|
|
119151
119314
|
}
|
|
119315
|
+
function fileUrlPath(value) {
|
|
119316
|
+
try {
|
|
119317
|
+
const url2 = new URL(value);
|
|
119318
|
+
if (url2.host)
|
|
119319
|
+
return INVALID_FILE_URL_PATH;
|
|
119320
|
+
return decodeURIComponent(url2.pathname);
|
|
119321
|
+
} catch {
|
|
119322
|
+
return INVALID_FILE_URL_PATH;
|
|
119323
|
+
}
|
|
119324
|
+
}
|
|
119152
119325
|
function extractUrls(input) {
|
|
119153
119326
|
const out = [];
|
|
119154
119327
|
walkStrings(input, (key, value) => {
|
|
@@ -119181,11 +119354,20 @@ function resolvePattern(pattern, cwd2) {
|
|
|
119181
119354
|
}
|
|
119182
119355
|
return path3.isAbsolute(pattern) ? path3.normalize(pattern) : path3.resolve(cwd2, pattern);
|
|
119183
119356
|
}
|
|
119357
|
+
var globRegexCache = /* @__PURE__ */ new Map();
|
|
119358
|
+
function compileGlob(pattern) {
|
|
119359
|
+
let re2 = globRegexCache.get(pattern);
|
|
119360
|
+
if (!re2) {
|
|
119361
|
+
const src = "^" + pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "<<DOUBLESTAR>>").replace(/\*/g, "[^/]*").replace(/<<DOUBLESTAR>>/g, ".*") + "$";
|
|
119362
|
+
re2 = new RegExp(src);
|
|
119363
|
+
globRegexCache.set(pattern, re2);
|
|
119364
|
+
}
|
|
119365
|
+
return re2;
|
|
119366
|
+
}
|
|
119184
119367
|
function matchesGlob(p3, pattern) {
|
|
119185
119368
|
if (pattern.endsWith("/**") && p3 === pattern.slice(0, -3))
|
|
119186
119369
|
return true;
|
|
119187
|
-
|
|
119188
|
-
return new RegExp(re2).test(p3);
|
|
119370
|
+
return compileGlob(pattern).test(p3);
|
|
119189
119371
|
}
|
|
119190
119372
|
function hostMatches(host, pattern) {
|
|
119191
119373
|
if (pattern.startsWith("*.")) {
|
|
@@ -119297,6 +119479,7 @@ var IsolatorRegistry2 = class {
|
|
|
119297
119479
|
}
|
|
119298
119480
|
};
|
|
119299
119481
|
var MAX_BROKER_OUTPUT_BYTES = 8 * 1024 * 1024;
|
|
119482
|
+
var BROKER_KILL_GRACE_MS = 2e3;
|
|
119300
119483
|
var MAX_FETCH_REDIRECTS = 5;
|
|
119301
119484
|
var BLOCKED_HANDLER_MODULES = Object.freeze([
|
|
119302
119485
|
"node:fs",
|
|
@@ -119589,12 +119772,21 @@ async function brokerExec(args, { caps, cwd: cwd2, signal }) {
|
|
|
119589
119772
|
const errChunks = [];
|
|
119590
119773
|
let total = 0;
|
|
119591
119774
|
let settled = false;
|
|
119592
|
-
|
|
119775
|
+
let killTimer = null;
|
|
119776
|
+
const terminate2 = () => {
|
|
119593
119777
|
child.kill("SIGTERM");
|
|
119778
|
+
if (killTimer)
|
|
119779
|
+
return;
|
|
119780
|
+
killTimer = setTimeout(() => child.kill("SIGKILL"), BROKER_KILL_GRACE_MS);
|
|
119781
|
+
killTimer.unref?.();
|
|
119782
|
+
};
|
|
119783
|
+
const timer = opts.timeoutMs ? setTimeout(() => {
|
|
119784
|
+
terminate2();
|
|
119594
119785
|
finish(() => reject(new Error(`[broker:exec] '${command}' exceeded ${opts.timeoutMs}ms`)));
|
|
119595
119786
|
}, opts.timeoutMs) : null;
|
|
119596
119787
|
const onAbort = () => {
|
|
119597
|
-
|
|
119788
|
+
terminate2();
|
|
119789
|
+
finish(() => reject(new Error(`[broker:exec] '${command}' aborted`)));
|
|
119598
119790
|
};
|
|
119599
119791
|
const finish = (settle) => {
|
|
119600
119792
|
if (settled)
|
|
@@ -119605,12 +119797,18 @@ async function brokerExec(args, { caps, cwd: cwd2, signal }) {
|
|
|
119605
119797
|
signal.removeEventListener("abort", onAbort);
|
|
119606
119798
|
settle();
|
|
119607
119799
|
};
|
|
119800
|
+
const clearKill = () => {
|
|
119801
|
+
if (killTimer) {
|
|
119802
|
+
clearTimeout(killTimer);
|
|
119803
|
+
killTimer = null;
|
|
119804
|
+
}
|
|
119805
|
+
};
|
|
119608
119806
|
const accumulate = (chunks, b3) => {
|
|
119609
119807
|
if (settled)
|
|
119610
119808
|
return;
|
|
119611
119809
|
total += b3.byteLength;
|
|
119612
119810
|
if (total > MAX_BROKER_OUTPUT_BYTES) {
|
|
119613
|
-
|
|
119811
|
+
terminate2();
|
|
119614
119812
|
finish(() => reject(new Error(`[broker:exec] '${command}' output exceeded the ${MAX_BROKER_OUTPUT_BYTES}-byte limit`)));
|
|
119615
119813
|
return;
|
|
119616
119814
|
}
|
|
@@ -119620,9 +119818,11 @@ async function brokerExec(args, { caps, cwd: cwd2, signal }) {
|
|
|
119620
119818
|
child.stderr.on("data", (b3) => accumulate(errChunks, b3));
|
|
119621
119819
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
119622
119820
|
child.on("error", (e3) => {
|
|
119821
|
+
clearKill();
|
|
119623
119822
|
finish(() => reject(e3));
|
|
119624
119823
|
});
|
|
119625
119824
|
child.on("close", (exitCode) => {
|
|
119825
|
+
clearKill();
|
|
119626
119826
|
finish(() => resolve12({
|
|
119627
119827
|
stdout: Buffer.concat(outChunks).toString("utf8"),
|
|
119628
119828
|
stderr: Buffer.concat(errChunks).toString("utf8"),
|
|
@@ -119899,7 +120099,8 @@ function createWorkerIsolator(opts = {}) {
|
|
|
119899
120099
|
let terminated = false;
|
|
119900
120100
|
const hardTerminate = () => {
|
|
119901
120101
|
terminated = true;
|
|
119902
|
-
|
|
120102
|
+
worker.terminate().catch(() => {
|
|
120103
|
+
});
|
|
119903
120104
|
};
|
|
119904
120105
|
const finish = (action, graceful = false) => {
|
|
119905
120106
|
if (settled)
|
|
@@ -120263,6 +120464,7 @@ async function invoke(call, caps, _signal) {
|
|
|
120263
120464
|
if (typeof exports.alloc !== "function") {
|
|
120264
120465
|
throw new Error(`[security:wasm] module does not export 'alloc(size: i32) -> i32'`);
|
|
120265
120466
|
}
|
|
120467
|
+
memoryHolder.alloc = (size) => exports.alloc(size);
|
|
120266
120468
|
const handler = exports[call.moduleRef.export];
|
|
120267
120469
|
if (typeof handler !== "function") {
|
|
120268
120470
|
throw new Error(`[security:wasm] export '${call.moduleRef.export}' is ${typeof handler}, expected function`);
|
|
@@ -120295,8 +120497,10 @@ function buildWasmHostImports(memoryHolder, caps, cwd2) {
|
|
|
120295
120497
|
return new TextDecoder().decode(new Uint8Array(memOf().buffer, ptr, len));
|
|
120296
120498
|
};
|
|
120297
120499
|
const sendBytes = (outPtrOut, outLenOut, bytes) => {
|
|
120298
|
-
const region = reserveScratch(memOf(), bytes.length);
|
|
120299
|
-
|
|
120500
|
+
const region = bytes.length === 0 ? SCRATCH_BASE : memoryHolder.alloc ? memoryHolder.alloc(bytes.length) : reserveScratch(memOf(), bytes.length);
|
|
120501
|
+
if (bytes.length > 0) {
|
|
120502
|
+
new Uint8Array(memOf().buffer, region, bytes.length).set(bytes);
|
|
120503
|
+
}
|
|
120300
120504
|
writePtrPair(memOf(), outPtrOut, outLenOut, region, bytes.length);
|
|
120301
120505
|
};
|
|
120302
120506
|
const sendStr = (outPtrOut, outLenOut, s2) => {
|
|
@@ -120337,13 +120541,13 @@ function buildWasmHostImports(memoryHolder, caps, cwd2) {
|
|
|
120337
120541
|
*/
|
|
120338
120542
|
broker_fs_write_file: (pathPtr, pathLen, dataPtr, dataLen, outPtrOut, outLenOut) => {
|
|
120339
120543
|
const filePath = readStr(pathPtr, pathLen);
|
|
120340
|
-
const data =
|
|
120544
|
+
const data = new Uint8Array(memOf().buffer, dataPtr, dataLen).slice();
|
|
120341
120545
|
if (!pathInScope(filePath, caps.fs, cwd2, "write")) {
|
|
120342
120546
|
return sendErr(outPtrOut, outLenOut, `[broker:fs.writeFile] path '${filePath}' is outside the tool's declared fs.write capability`);
|
|
120343
120547
|
}
|
|
120344
120548
|
try {
|
|
120345
120549
|
mkdirSync(path3.dirname(filePath), { recursive: true });
|
|
120346
|
-
writeFileSync(filePath, data
|
|
120550
|
+
writeFileSync(filePath, Buffer$1.from(data));
|
|
120347
120551
|
writePtrPair(memOf(), outPtrOut, outLenOut, 0, 0);
|
|
120348
120552
|
return SUCCESS;
|
|
120349
120553
|
} catch (e3) {
|
|
@@ -124450,6 +124654,8 @@ var AnthropicProvider = class {
|
|
|
124450
124654
|
const parts = [this.oauth?.systemPreamble, system, req.system].filter((s2) => typeof s2 === "string" && s2.length > 0);
|
|
124451
124655
|
const systemForCount = parts.length > 0 ? parts.join("\n\n") : void 0;
|
|
124452
124656
|
try {
|
|
124657
|
+
if (this.oauth)
|
|
124658
|
+
await this.ensureFreshOauth();
|
|
124453
124659
|
const result = await this.client.messages.countTokens({
|
|
124454
124660
|
model: req.model || this.defaultModel,
|
|
124455
124661
|
...systemForCount !== void 0 ? { system: systemForCount } : {},
|
|
@@ -130898,7 +131104,16 @@ var OpenAIProvider = class {
|
|
|
130898
131104
|
...tools ? { tools } : {},
|
|
130899
131105
|
...req.temperature !== void 0 ? { temperature: req.temperature } : {},
|
|
130900
131106
|
...req.maxTokens ? { [tokenLimitKey]: req.maxTokens } : {},
|
|
130901
|
-
|
|
131107
|
+
// Send `reasoning_effort` independently of the token-field heuristic.
|
|
131108
|
+
// The two are unrelated concerns: `usesCompletionTokens` picks the cap
|
|
131109
|
+
// FIELD NAME for the OpenAI-hosted reasoning models, while effort applies
|
|
131110
|
+
// to any reasoning backend. OpenAI-compatible vendors (z.ai GLM,
|
|
131111
|
+
// DeepSeek-R1, vLLM, Ollama) honor reasoning_effort but their model ids
|
|
131112
|
+
// never match the gpt-5/o1/o3 regex, so gating effort on it silently
|
|
131113
|
+
// dropped a user-requested effort for exactly those backends. The
|
|
131114
|
+
// descriptor's `supportsReasoning` already gates this upstream via
|
|
131115
|
+
// req.reasoning.
|
|
131116
|
+
...emitReasoning && reasoningEffort ? { reasoning_effort: reasoningEffort } : {},
|
|
130902
131117
|
stream: true,
|
|
130903
131118
|
// OpenAI only emits the final `usage` chunk when this is set;
|
|
130904
131119
|
// without it `raw.usage` is null on every chunk and token usage
|
|
@@ -131162,7 +131377,6 @@ function waitForCallback(opts) {
|
|
|
131162
131377
|
if (err) {
|
|
131163
131378
|
res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
|
|
131164
131379
|
res.end(htmlPage("OAuth error", `${err}${errDesc ? `: ${errDesc}` : ""}`));
|
|
131165
|
-
clearTimeout(timer);
|
|
131166
131380
|
const denied = err === "access_denied";
|
|
131167
131381
|
settle(() => reject(new MoxxyError({
|
|
131168
131382
|
code: denied ? "OAUTH_FLOW_DENIED" : "AUTH_INVALID",
|
|
@@ -131177,7 +131391,6 @@ function waitForCallback(opts) {
|
|
|
131177
131391
|
if (!code || !returnedState) {
|
|
131178
131392
|
res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
|
|
131179
131393
|
res.end(htmlPage("OAuth error", "callback was missing code or state"));
|
|
131180
|
-
clearTimeout(timer);
|
|
131181
131394
|
settle(() => reject(new MoxxyError({
|
|
131182
131395
|
code: "AUTH_INVALID",
|
|
131183
131396
|
message: "OAuth callback was missing code or state \u2014 the upstream redirect is malformed.",
|
|
@@ -131188,7 +131401,6 @@ function waitForCallback(opts) {
|
|
|
131188
131401
|
if (returnedState !== opts.expectedState) {
|
|
131189
131402
|
res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
|
|
131190
131403
|
res.end(htmlPage("OAuth error", "state mismatch \u2014 possible CSRF, refusing"));
|
|
131191
|
-
clearTimeout(timer);
|
|
131192
131404
|
settle(() => reject(new MoxxyError({
|
|
131193
131405
|
code: "OAUTH_FLOW_STATE_MISMATCH",
|
|
131194
131406
|
message: "OAuth state mismatch \u2014 possible CSRF attempt, refusing to continue.",
|
|
@@ -131246,7 +131458,8 @@ async function exchangeCodeForToken(input, fetchImpl2 = fetch) {
|
|
|
131246
131458
|
const res = await fetchImpl2(input.tokenUrl, {
|
|
131247
131459
|
method: "POST",
|
|
131248
131460
|
headers: { "Content-Type": "application/x-www-form-urlencoded", Accept: "application/json" },
|
|
131249
|
-
body: body.toString()
|
|
131461
|
+
body: body.toString(),
|
|
131462
|
+
...input.signal ? { signal: input.signal } : {}
|
|
131250
131463
|
});
|
|
131251
131464
|
if (!res.ok) {
|
|
131252
131465
|
const text = await res.text().catch(() => "");
|
|
@@ -131362,7 +131575,10 @@ async function runAuthorizationCodeFlow(opts) {
|
|
|
131362
131575
|
});
|
|
131363
131576
|
}
|
|
131364
131577
|
async function pollUntil(fn, opts) {
|
|
131365
|
-
const state = {
|
|
131578
|
+
const state = {
|
|
131579
|
+
intervalMs: opts.intervalMs,
|
|
131580
|
+
...opts.signal ? { signal: opts.signal } : {}
|
|
131581
|
+
};
|
|
131366
131582
|
const leadingWait = opts.leadingWait ?? true;
|
|
131367
131583
|
const deadline = Date.now() + opts.timeoutMs;
|
|
131368
131584
|
const label3 = opts.label ?? "poll";
|
|
@@ -131512,7 +131728,8 @@ async function pollOnce(opts, deviceCode, state) {
|
|
|
131512
131728
|
const pollRes = await fetch(opts.tokenUrl, {
|
|
131513
131729
|
method: "POST",
|
|
131514
131730
|
headers: { "Content-Type": "application/x-www-form-urlencoded", Accept: "application/json" },
|
|
131515
|
-
body: body.toString()
|
|
131731
|
+
body: body.toString(),
|
|
131732
|
+
...state.signal ? { signal: state.signal } : {}
|
|
131516
131733
|
});
|
|
131517
131734
|
const pollJson = await pollRes.json().catch(() => ({}));
|
|
131518
131735
|
return classifyDeviceTokenResponse(pollRes, pollJson, state);
|
|
@@ -132110,12 +132327,13 @@ function openaiDeviceFlow(opts) {
|
|
|
132110
132327
|
}
|
|
132111
132328
|
};
|
|
132112
132329
|
},
|
|
132113
|
-
async poll(init3,
|
|
132330
|
+
async poll(init3, state) {
|
|
132114
132331
|
const { deviceAuthId, userCode, clientId } = init3.providerData;
|
|
132115
132332
|
const res = await fetch(pollUrl, {
|
|
132116
132333
|
method: "POST",
|
|
132117
132334
|
headers: { "Content-Type": "application/json" },
|
|
132118
|
-
body: JSON.stringify({ device_auth_id: deviceAuthId, user_code: userCode })
|
|
132335
|
+
body: JSON.stringify({ device_auth_id: deviceAuthId, user_code: userCode }),
|
|
132336
|
+
...state.signal ? { signal: state.signal } : {}
|
|
132119
132337
|
});
|
|
132120
132338
|
if (res.ok) {
|
|
132121
132339
|
const data = await res.json();
|
|
@@ -132125,7 +132343,8 @@ function openaiDeviceFlow(opts) {
|
|
|
132125
132343
|
code: data.authorization_code,
|
|
132126
132344
|
redirectUri: exchangeRedirectUri,
|
|
132127
132345
|
clientId,
|
|
132128
|
-
codeVerifier: data.code_verifier
|
|
132346
|
+
codeVerifier: data.code_verifier,
|
|
132347
|
+
...state.signal ? { signal: state.signal } : {}
|
|
132129
132348
|
})
|
|
132130
132349
|
};
|
|
132131
132350
|
}
|
|
@@ -132547,9 +132766,16 @@ async function* consumeResponsesSse(body, signal, emitReasoning = false) {
|
|
|
132547
132766
|
if (errored)
|
|
132548
132767
|
return;
|
|
132549
132768
|
for (const entry of pending2.values()) {
|
|
132769
|
+
const outId = entry.callId || entry.id;
|
|
132770
|
+
if (!entry.emittedStart && entry.name) {
|
|
132771
|
+
entry.emittedStart = true;
|
|
132772
|
+
yield { type: "tool_use_start", id: outId, name: entry.name };
|
|
132773
|
+
}
|
|
132550
132774
|
if (entry.emittedStart) {
|
|
132551
132775
|
sawToolCall = true;
|
|
132552
|
-
yield { type: "tool_use_end", id:
|
|
132776
|
+
yield { type: "tool_use_end", id: outId, input: parseToolArgs(entry.args) };
|
|
132777
|
+
} else if (process.env.MOXXY_DEBUG) {
|
|
132778
|
+
console.error(`[openai-codex] dropping a truncated function_call with no name (id=${outId}, args=${entry.args.length}B)`);
|
|
132553
132779
|
}
|
|
132554
132780
|
}
|
|
132555
132781
|
if (stopReason === "end_turn" && sawToolCall) {
|
|
@@ -132629,6 +132855,14 @@ var CodexProvider = class {
|
|
|
132629
132855
|
yield toErrorEvent(err);
|
|
132630
132856
|
return;
|
|
132631
132857
|
}
|
|
132858
|
+
if (response.status === 401) {
|
|
132859
|
+
yield {
|
|
132860
|
+
type: "error",
|
|
132861
|
+
message: "ChatGPT OAuth credentials were rejected after a token refresh. Run `moxxy login openai-codex` to re-authenticate.",
|
|
132862
|
+
retryable: false
|
|
132863
|
+
};
|
|
132864
|
+
return;
|
|
132865
|
+
}
|
|
132632
132866
|
}
|
|
132633
132867
|
if (!response.ok || !response.body) {
|
|
132634
132868
|
const text = await response.text().catch(() => "");
|
|
@@ -133253,9 +133487,6 @@ var localPlugin = definePlugin({
|
|
|
133253
133487
|
version: "0.0.0",
|
|
133254
133488
|
providers: [localProviderDef]
|
|
133255
133489
|
});
|
|
133256
|
-
|
|
133257
|
-
// ../plugin-stt-whisper/dist/audio.js
|
|
133258
|
-
var MOXXY_PCM16_24KHZ_MIME = "audio/x-moxxy-pcm16-24khz";
|
|
133259
133490
|
var WHISPER_FILENAME_BY_MIME = {
|
|
133260
133491
|
"audio/ogg": "audio.ogg",
|
|
133261
133492
|
"audio/opus": "audio.opus",
|
|
@@ -134823,6 +135054,18 @@ async function* runGoalMode(ctx) {
|
|
|
134823
135054
|
continue;
|
|
134824
135055
|
}
|
|
134825
135056
|
reactiveCompactions = 0;
|
|
135057
|
+
if (reasoning) {
|
|
135058
|
+
yield await ctx.emit({
|
|
135059
|
+
type: "reasoning_message",
|
|
135060
|
+
sessionId: ctx.sessionId,
|
|
135061
|
+
turnId: ctx.turnId,
|
|
135062
|
+
source: "model",
|
|
135063
|
+
content: reasoning.text,
|
|
135064
|
+
...reasoning.signature ? { signature: reasoning.signature } : {},
|
|
135065
|
+
...reasoning.redacted ? { redacted: true } : {},
|
|
135066
|
+
...reasoning.encrypted ? { encrypted: reasoning.encrypted } : {}
|
|
135067
|
+
});
|
|
135068
|
+
}
|
|
134826
135069
|
if (totalTokens > GOAL_TOKEN_BUDGET) {
|
|
134827
135070
|
yield await ctx.emit({
|
|
134828
135071
|
type: "plugin_event",
|
|
@@ -134843,18 +135086,6 @@ async function* runGoalMode(ctx) {
|
|
|
134843
135086
|
});
|
|
134844
135087
|
return;
|
|
134845
135088
|
}
|
|
134846
|
-
if (reasoning) {
|
|
134847
|
-
yield await ctx.emit({
|
|
134848
|
-
type: "reasoning_message",
|
|
134849
|
-
sessionId: ctx.sessionId,
|
|
134850
|
-
turnId: ctx.turnId,
|
|
134851
|
-
source: "model",
|
|
134852
|
-
content: reasoning.text,
|
|
134853
|
-
...reasoning.signature ? { signature: reasoning.signature } : {},
|
|
134854
|
-
...reasoning.redacted ? { redacted: true } : {},
|
|
134855
|
-
...reasoning.encrypted ? { encrypted: reasoning.encrypted } : {}
|
|
134856
|
-
});
|
|
134857
|
-
}
|
|
134858
135089
|
const stuck = yield* emitRequestsAndDetectStuck(ctx, toolUses, detector, {
|
|
134859
135090
|
abortedResultMessage: "goal mode aborted (stuck pattern) before this call ran",
|
|
134860
135091
|
nearHint: "against the same target (only volatile args varied)",
|
|
@@ -135455,6 +135686,16 @@ async function* runDeepResearchMode(ctx) {
|
|
|
135455
135686
|
const synthesisText = await collectSynthesis(ctx, synthesisInput);
|
|
135456
135687
|
if (synthesisText === null)
|
|
135457
135688
|
return;
|
|
135689
|
+
if (ctx.signal.aborted) {
|
|
135690
|
+
yield await ctx.emit({
|
|
135691
|
+
type: "abort",
|
|
135692
|
+
sessionId: ctx.sessionId,
|
|
135693
|
+
turnId: ctx.turnId,
|
|
135694
|
+
source: "system",
|
|
135695
|
+
reason: "aborted during synthesis"
|
|
135696
|
+
});
|
|
135697
|
+
return;
|
|
135698
|
+
}
|
|
135458
135699
|
yield await ctx.emit({
|
|
135459
135700
|
type: "assistant_message",
|
|
135460
135701
|
sessionId: ctx.sessionId,
|
|
@@ -135907,11 +136148,10 @@ async function createUnixSocketServer(socketPath, logger = stderrLogger) {
|
|
|
135907
136148
|
} else {
|
|
135908
136149
|
warnWindowsPipeAclOnce(logger, socketPath);
|
|
135909
136150
|
}
|
|
135910
|
-
|
|
136151
|
+
let connectionHandler;
|
|
135911
136152
|
const server = net2.createServer((socket) => {
|
|
135912
136153
|
const transport = new NdjsonTransport(socket);
|
|
135913
|
-
|
|
135914
|
-
handler(transport);
|
|
136154
|
+
connectionHandler?.(transport);
|
|
135915
136155
|
});
|
|
135916
136156
|
await new Promise((resolve12, reject) => {
|
|
135917
136157
|
server.once("error", reject);
|
|
@@ -135933,7 +136173,7 @@ async function createUnixSocketServer(socketPath, logger = stderrLogger) {
|
|
|
135933
136173
|
return {
|
|
135934
136174
|
address: socketPath,
|
|
135935
136175
|
onConnection(handler) {
|
|
135936
|
-
|
|
136176
|
+
connectionHandler = handler;
|
|
135937
136177
|
},
|
|
135938
136178
|
close() {
|
|
135939
136179
|
return new Promise((resolve12) => {
|
|
@@ -138842,6 +139082,57 @@ async function integrate(input) {
|
|
|
138842
139082
|
}
|
|
138843
139083
|
return { merged, conflicts, resolvedByOwnership, stagingBranch: branchName, promoted };
|
|
138844
139084
|
}
|
|
139085
|
+
var COLLAB_LOCK_PATH = join(homedir(), ".moxxy", "collab", "active.lock");
|
|
139086
|
+
function collabLockPath() {
|
|
139087
|
+
return process.env.MOXXY_COLLAB_LOCK || COLLAB_LOCK_PATH;
|
|
139088
|
+
}
|
|
139089
|
+
function readRaw() {
|
|
139090
|
+
try {
|
|
139091
|
+
return JSON.parse(readFileSync(collabLockPath(), "utf8"));
|
|
139092
|
+
} catch {
|
|
139093
|
+
return null;
|
|
139094
|
+
}
|
|
139095
|
+
}
|
|
139096
|
+
function isAlive(pid) {
|
|
139097
|
+
try {
|
|
139098
|
+
process.kill(pid, 0);
|
|
139099
|
+
return true;
|
|
139100
|
+
} catch (err) {
|
|
139101
|
+
return err.code === "EPERM";
|
|
139102
|
+
}
|
|
139103
|
+
}
|
|
139104
|
+
function readActiveCollab() {
|
|
139105
|
+
const info = readRaw();
|
|
139106
|
+
if (!info)
|
|
139107
|
+
return null;
|
|
139108
|
+
if (!isAlive(info.pid)) {
|
|
139109
|
+
try {
|
|
139110
|
+
unlinkSync(collabLockPath());
|
|
139111
|
+
} catch {
|
|
139112
|
+
}
|
|
139113
|
+
return null;
|
|
139114
|
+
}
|
|
139115
|
+
return info;
|
|
139116
|
+
}
|
|
139117
|
+
function tryAcquireCollabLock(args) {
|
|
139118
|
+
mkdirSync(dirname(collabLockPath()), { recursive: true });
|
|
139119
|
+
const existing = readActiveCollab();
|
|
139120
|
+
if (existing && existing.sessionId !== args.sessionId) {
|
|
139121
|
+
return { ok: false, holder: existing };
|
|
139122
|
+
}
|
|
139123
|
+
const info = { pid: process.pid, ...args };
|
|
139124
|
+
writeFileSync(collabLockPath(), JSON.stringify(info));
|
|
139125
|
+
return { ok: true };
|
|
139126
|
+
}
|
|
139127
|
+
function releaseCollabLock(sessionId) {
|
|
139128
|
+
const info = readRaw();
|
|
139129
|
+
if (info && info.sessionId === sessionId) {
|
|
139130
|
+
try {
|
|
139131
|
+
unlinkSync(collabLockPath());
|
|
139132
|
+
} catch {
|
|
139133
|
+
}
|
|
139134
|
+
}
|
|
139135
|
+
}
|
|
138845
139136
|
|
|
138846
139137
|
// ../mode-collaborative/dist/collab-loop.js
|
|
138847
139138
|
var POLL_MS = 500;
|
|
@@ -138860,47 +139151,56 @@ async function* runCollaborative(ctx, deps) {
|
|
|
138860
139151
|
yield await ctx.emit(assistant(ctx, "Collaborative mode needs a task to work on."));
|
|
138861
139152
|
return;
|
|
138862
139153
|
}
|
|
138863
|
-
const
|
|
138864
|
-
|
|
138865
|
-
|
|
138866
|
-
|
|
138867
|
-
|
|
138868
|
-
yield await ctx.emit(plugin4(ctx, "collab_fallback_sequential", {
|
|
138869
|
-
reason: !gitInstalled2 ? "git is not installed \u2014 running agents sequentially in your workspace" : !gitRepo ? "this folder is not a git repository \u2014 running agents sequentially in your workspace" : "sequential mode selected"
|
|
138870
|
-
}));
|
|
138871
|
-
}
|
|
138872
|
-
let baseSha = "";
|
|
138873
|
-
if (parallel) {
|
|
138874
|
-
const base2 = await resolveBase(cwd2);
|
|
138875
|
-
baseSha = base2.baseSha;
|
|
139154
|
+
const lock = tryAcquireCollabLock({ sessionId: String(ctx.sessionId), task, startedAtMs: Date.now() });
|
|
139155
|
+
if (!lock.ok) {
|
|
139156
|
+
yield await ctx.emit(plugin4(ctx, "collab_blocked", { reason: "already-running", holderTask: lock.holder.task }));
|
|
139157
|
+
yield await ctx.emit(assistant(ctx, `A collaboration is already running ("${lock.holder.task}"). Only one runs at a time to save resources \u2014 stop it first, then start again.`));
|
|
139158
|
+
return;
|
|
138876
139159
|
}
|
|
139160
|
+
const runId = collabRunId(String(ctx.sessionId), String(ctx.turnId));
|
|
138877
139161
|
const worktrees = /* @__PURE__ */ new Map();
|
|
138878
|
-
|
|
138879
|
-
|
|
138880
|
-
|
|
138881
|
-
role: "architect",
|
|
138882
|
-
subtask: task
|
|
138883
|
-
};
|
|
138884
|
-
const hub = await createCollaborationHub({
|
|
138885
|
-
socketPath: hubSocketPath(runId),
|
|
138886
|
-
task,
|
|
138887
|
-
roster: [architectEntry],
|
|
138888
|
-
peerReader: peerReaderFor(worktrees, baseSha)
|
|
138889
|
-
});
|
|
138890
|
-
registerActiveHub(String(ctx.sessionId), hub);
|
|
138891
|
-
const unsubscribe = hub.subscribe((e3) => {
|
|
138892
|
-
void ctx.emit(toCollabEvent(ctx, e3));
|
|
138893
|
-
});
|
|
138894
|
-
const supervisorOpts = {
|
|
138895
|
-
runId,
|
|
138896
|
-
hubSocket: hub.socketPath,
|
|
138897
|
-
coordinatorSessionId: String(ctx.sessionId),
|
|
138898
|
-
parentTask: task,
|
|
138899
|
-
...cfg.defaultModel ? { defaultModel: cfg.defaultModel } : {},
|
|
138900
|
-
signal: ctx.signal
|
|
138901
|
-
};
|
|
138902
|
-
const supervisor = (deps.createSupervisor ?? ((o2) => new PeerSupervisor(o2)))(supervisorOpts, hub);
|
|
139162
|
+
let hub = null;
|
|
139163
|
+
let supervisor = null;
|
|
139164
|
+
let unsubscribe = null;
|
|
138903
139165
|
try {
|
|
139166
|
+
mkdirSync(collabRunDir(runId), { recursive: true });
|
|
139167
|
+
const { installed: gitInstalled2, repo: gitRepo } = await detectGit(cwd2);
|
|
139168
|
+
const parallel = cfg.concurrency === "parallel" && gitRepo;
|
|
139169
|
+
if (!parallel) {
|
|
139170
|
+
yield await ctx.emit(plugin4(ctx, "collab_fallback_sequential", {
|
|
139171
|
+
reason: !gitInstalled2 ? "git is not installed \u2014 running agents sequentially in your workspace" : !gitRepo ? "this folder is not a git repository \u2014 running agents sequentially in your workspace" : "sequential mode selected"
|
|
139172
|
+
}));
|
|
139173
|
+
}
|
|
139174
|
+
let baseSha = "";
|
|
139175
|
+
if (parallel) {
|
|
139176
|
+
const base2 = await resolveBase(cwd2, { snapshotDirty: true });
|
|
139177
|
+
baseSha = base2.baseSha;
|
|
139178
|
+
}
|
|
139179
|
+
const architectEntry = {
|
|
139180
|
+
id: ARCHITECT_AGENT_ID,
|
|
139181
|
+
name: "Architect",
|
|
139182
|
+
role: "architect",
|
|
139183
|
+
subtask: task
|
|
139184
|
+
};
|
|
139185
|
+
hub = await createCollaborationHub({
|
|
139186
|
+
socketPath: hubSocketPath(runId),
|
|
139187
|
+
task,
|
|
139188
|
+
roster: [architectEntry],
|
|
139189
|
+
peerReader: peerReaderFor(worktrees, baseSha)
|
|
139190
|
+
});
|
|
139191
|
+
registerActiveHub(String(ctx.sessionId), hub);
|
|
139192
|
+
unsubscribe = hub.subscribe((e3) => {
|
|
139193
|
+
void ctx.emit(toCollabEvent(ctx, e3));
|
|
139194
|
+
});
|
|
139195
|
+
const supervisorOpts = {
|
|
139196
|
+
runId,
|
|
139197
|
+
hubSocket: hub.socketPath,
|
|
139198
|
+
coordinatorSessionId: String(ctx.sessionId),
|
|
139199
|
+
parentTask: task,
|
|
139200
|
+
...cfg.defaultModel ? { defaultModel: cfg.defaultModel } : {},
|
|
139201
|
+
signal: ctx.signal
|
|
139202
|
+
};
|
|
139203
|
+
supervisor = (deps.createSupervisor ?? ((o2) => new PeerSupervisor(o2)))(supervisorOpts, hub);
|
|
138904
139204
|
yield await ctx.emit(plugin4(ctx, "collab_started", { task, parallel, gitInstalled: gitInstalled2, gitRepo }));
|
|
138905
139205
|
supervisor.spawn({ entry: architectEntry, cwd: cwd2, mode: COLLAB_ARCHITECT_MODE_NAME });
|
|
138906
139206
|
yield await ctx.emit(plugin4(ctx, "collab_agent_spawned", { id: ARCHITECT_AGENT_ID, role: "architect" }));
|
|
@@ -138998,10 +139298,14 @@ ${summaryBlock}${mergeNote ? `
|
|
|
138998
139298
|
${mergeNote}` : ""}`));
|
|
138999
139299
|
yield await ctx.emit(plugin4(ctx, "collab_completed", { done: doneIds, total: roster.length }));
|
|
139000
139300
|
} finally {
|
|
139001
|
-
|
|
139002
|
-
|
|
139301
|
+
if (supervisor)
|
|
139302
|
+
await supervisor.shutdownAll("collaboration complete");
|
|
139303
|
+
if (unsubscribe)
|
|
139304
|
+
unsubscribe();
|
|
139003
139305
|
unregisterActiveHub(String(ctx.sessionId));
|
|
139004
|
-
|
|
139306
|
+
if (hub)
|
|
139307
|
+
await hub.close();
|
|
139308
|
+
releaseCollabLock(String(ctx.sessionId));
|
|
139005
139309
|
}
|
|
139006
139310
|
}
|
|
139007
139311
|
function lastUserPromptText(ctx) {
|
|
@@ -139655,6 +139959,7 @@ var import_grammy6 = __toESM(require_mod2());
|
|
|
139655
139959
|
|
|
139656
139960
|
// ../plugin-telegram/dist/channel.js
|
|
139657
139961
|
var import_grammy5 = __toESM(require_mod2());
|
|
139962
|
+
init_dist();
|
|
139658
139963
|
|
|
139659
139964
|
// ../plugin-telegram/dist/permission.js
|
|
139660
139965
|
var TelegramPermissionResolver = class {
|
|
@@ -140111,7 +140416,16 @@ function composeFrame(snap) {
|
|
|
140111
140416
|
return parts.join("\n\n");
|
|
140112
140417
|
}
|
|
140113
140418
|
function stripHtml(html) {
|
|
140114
|
-
return html.replace(/<\/?[a-z][^>]*>/gi, "").replace(
|
|
140419
|
+
return html.replace(/<\/?[a-z][^>]*>/gi, "").replace(/&#(\d+);/g, (_2, dec) => safeFromCodePoint(parseInt(dec, 10))).replace(/&#x([0-9a-f]+);/gi, (_2, hex) => safeFromCodePoint(parseInt(hex, 16))).replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/&/g, "&");
|
|
140420
|
+
}
|
|
140421
|
+
function safeFromCodePoint(cp) {
|
|
140422
|
+
if (!Number.isFinite(cp) || cp < 0 || cp > 1114111)
|
|
140423
|
+
return "";
|
|
140424
|
+
try {
|
|
140425
|
+
return String.fromCodePoint(cp);
|
|
140426
|
+
} catch {
|
|
140427
|
+
return "";
|
|
140428
|
+
}
|
|
140115
140429
|
}
|
|
140116
140430
|
function truncate3(s2, n2) {
|
|
140117
140431
|
return s2.length <= n2 ? s2 : s2.slice(0, n2) + "\u2026";
|
|
@@ -140882,13 +141196,11 @@ function mapChoice(choice) {
|
|
|
140882
141196
|
}
|
|
140883
141197
|
|
|
140884
141198
|
// ../plugin-telegram/dist/channel/turn-runner.js
|
|
140885
|
-
init_dist();
|
|
140886
141199
|
async function runUserTurn(ctx, deps, opts) {
|
|
140887
141200
|
const { session, bot, framePump, typing, logger } = deps;
|
|
140888
|
-
const { chatId, text, model, controller } = opts;
|
|
141201
|
+
const { chatId, text, model, controller, turnId } = opts;
|
|
140889
141202
|
framePump.beginTurn(chatId);
|
|
140890
141203
|
typing.start(bot, chatId);
|
|
140891
|
-
const turnId = newTurnId();
|
|
140892
141204
|
const unsubscribe = session.log.subscribe((event) => {
|
|
140893
141205
|
if (event.turnId !== turnId)
|
|
140894
141206
|
return;
|
|
@@ -141089,6 +141401,12 @@ var TelegramChannel = class {
|
|
|
141089
141401
|
// without poisoning the session-level signal (which other channels
|
|
141090
141402
|
// sharing the same Session would also observe).
|
|
141091
141403
|
turnController = null;
|
|
141404
|
+
// turnIds of turns THIS channel initiated. mirrorForeignTurn filters on these
|
|
141405
|
+
// (invariant #8: filter event-log subscribers by turnId when multiplexing
|
|
141406
|
+
// turns on one Session) rather than the coarse `busy` flag alone — so an
|
|
141407
|
+
// assistant_message dispatched for our own turn AFTER `busy` flips false
|
|
141408
|
+
// (async event ordering / RemoteSession replay) isn't re-mirrored as foreign.
|
|
141409
|
+
ownTurnIds = /* @__PURE__ */ new Set();
|
|
141092
141410
|
// When a user clicks an approval option that needs text follow-up
|
|
141093
141411
|
// (e.g. plan-execute "Redraft with feedback"), we stash the
|
|
141094
141412
|
// approval+option pair and capture the user's NEXT message as the
|
|
@@ -141234,6 +141552,13 @@ var TelegramChannel = class {
|
|
|
141234
141552
|
const controller = new AbortController();
|
|
141235
141553
|
this.turnController = controller;
|
|
141236
141554
|
const effectiveModel = this.activeModelOverride ?? this.model;
|
|
141555
|
+
const turnId = newTurnId();
|
|
141556
|
+
this.ownTurnIds.add(turnId);
|
|
141557
|
+
if (this.ownTurnIds.size > 64) {
|
|
141558
|
+
const oldest = this.ownTurnIds.values().next().value;
|
|
141559
|
+
if (oldest !== void 0)
|
|
141560
|
+
this.ownTurnIds.delete(oldest);
|
|
141561
|
+
}
|
|
141237
141562
|
try {
|
|
141238
141563
|
await runUserTurn(ctx, {
|
|
141239
141564
|
session: this.session,
|
|
@@ -141241,7 +141566,7 @@ var TelegramChannel = class {
|
|
|
141241
141566
|
framePump: this.framePump,
|
|
141242
141567
|
typing: this.typing,
|
|
141243
141568
|
...this.opts.logger ? { logger: this.opts.logger } : {}
|
|
141244
|
-
}, { chatId, text, model: effectiveModel, controller });
|
|
141569
|
+
}, { chatId, text, model: effectiveModel, controller, turnId });
|
|
141245
141570
|
} finally {
|
|
141246
141571
|
this.busy = false;
|
|
141247
141572
|
this.turnController = null;
|
|
@@ -141255,7 +141580,11 @@ var TelegramChannel = class {
|
|
|
141255
141580
|
* avoid parse-mode pitfalls; the view itself lives on the web surface.
|
|
141256
141581
|
*/
|
|
141257
141582
|
mirrorForeignTurn(event) {
|
|
141258
|
-
if (event.type !== "assistant_message"
|
|
141583
|
+
if (event.type !== "assistant_message")
|
|
141584
|
+
return;
|
|
141585
|
+
if (this.ownTurnIds.has(event.turnId))
|
|
141586
|
+
return;
|
|
141587
|
+
if (this.busy)
|
|
141259
141588
|
return;
|
|
141260
141589
|
if (!this.bot || this.lastChatId == null)
|
|
141261
141590
|
return;
|
|
@@ -142332,10 +142661,16 @@ async function runPairFlow(ctx) {
|
|
|
142332
142661
|
await session.close("SIGINT").catch(() => void 0);
|
|
142333
142662
|
process.exit(0);
|
|
142334
142663
|
};
|
|
142335
|
-
|
|
142336
|
-
process.
|
|
142337
|
-
|
|
142338
|
-
|
|
142664
|
+
const onSignal = () => void shutdown();
|
|
142665
|
+
process.once("SIGINT", onSignal);
|
|
142666
|
+
process.once("SIGTERM", onSignal);
|
|
142667
|
+
try {
|
|
142668
|
+
await handle2.running;
|
|
142669
|
+
return 0;
|
|
142670
|
+
} finally {
|
|
142671
|
+
process.removeListener("SIGINT", onSignal);
|
|
142672
|
+
process.removeListener("SIGTERM", onSignal);
|
|
142673
|
+
}
|
|
142339
142674
|
}
|
|
142340
142675
|
|
|
142341
142676
|
// ../plugin-telegram/dist/setup-wizard.js
|
|
@@ -142582,8 +142917,8 @@ function buildTelegramPlugin(opts) {
|
|
|
142582
142917
|
if (!targetChat) {
|
|
142583
142918
|
throw new Error("no authorized chat \u2014 run `moxxy channels telegram pair` first or pass `chatId` explicitly");
|
|
142584
142919
|
}
|
|
142585
|
-
const
|
|
142586
|
-
await
|
|
142920
|
+
const api = new import_grammy6.Api(token);
|
|
142921
|
+
await api.sendMessage(targetChat, text, parseMode ? { parse_mode: parseMode } : {});
|
|
142587
142922
|
return { delivered: true, chatId: targetChat, length: text.length };
|
|
142588
142923
|
}
|
|
142589
142924
|
}),
|
|
@@ -142816,6 +143151,9 @@ var HttpChannel = class {
|
|
|
142816
143151
|
this.permissionResolver = opts.allowedTools && opts.allowedTools.length > 0 ? createAllowListResolver([...opts.allowedTools]) : denyByDefaultResolver;
|
|
142817
143152
|
}
|
|
142818
143153
|
async start(startOpts) {
|
|
143154
|
+
if (this.server) {
|
|
143155
|
+
throw new Error("HttpChannel is already started \u2014 call stop() before starting again.");
|
|
143156
|
+
}
|
|
142819
143157
|
const ctx = {
|
|
142820
143158
|
session: startOpts.session,
|
|
142821
143159
|
authToken: this.authToken,
|
|
@@ -142874,11 +143212,16 @@ var HttpChannel = class {
|
|
|
142874
143212
|
return {
|
|
142875
143213
|
running,
|
|
142876
143214
|
stop: async () => {
|
|
143215
|
+
const srv = this.server;
|
|
142877
143216
|
await new Promise((resolve12) => {
|
|
142878
|
-
if (!
|
|
143217
|
+
if (!srv)
|
|
142879
143218
|
return resolve12();
|
|
142880
|
-
|
|
143219
|
+
srv.close(() => resolve12());
|
|
142881
143220
|
});
|
|
143221
|
+
if (this.server === srv) {
|
|
143222
|
+
this.server = null;
|
|
143223
|
+
this.boundPortValue = 0;
|
|
143224
|
+
}
|
|
142882
143225
|
}
|
|
142883
143226
|
};
|
|
142884
143227
|
}
|
|
@@ -143052,13 +143395,19 @@ async function pidCommand2(pid) {
|
|
|
143052
143395
|
function looksLikeMoxxy(command) {
|
|
143053
143396
|
return command.length > 0 && /moxxy/i.test(command);
|
|
143054
143397
|
}
|
|
143055
|
-
|
|
143398
|
+
var realFreePortDeps = {
|
|
143399
|
+
pidsListeningOn,
|
|
143400
|
+
pidCommand: pidCommand2,
|
|
143401
|
+
kill: (pid, signal) => process.kill(pid, signal),
|
|
143402
|
+
graceMs: 400
|
|
143403
|
+
};
|
|
143404
|
+
async function freeTcpPortIfMoxxy(port, logger, deps = realFreePortDeps) {
|
|
143056
143405
|
if (process.platform === "win32")
|
|
143057
143406
|
return false;
|
|
143058
|
-
const pids = (await pidsListeningOn(port)).filter((pid) => pid !== process.pid);
|
|
143407
|
+
const pids = (await deps.pidsListeningOn(port)).filter((pid) => pid !== process.pid);
|
|
143059
143408
|
if (pids.length === 0)
|
|
143060
143409
|
return false;
|
|
143061
|
-
const holders = await Promise.all(pids.map(async (pid) => ({ pid, command: await
|
|
143410
|
+
const holders = await Promise.all(pids.map(async (pid) => ({ pid, command: await deps.pidCommand(pid) })));
|
|
143062
143411
|
const foreign = holders.filter((h3) => !looksLikeMoxxy(h3.command));
|
|
143063
143412
|
if (foreign.length > 0) {
|
|
143064
143413
|
logger?.warn?.(`port ${port} is held by non-moxxy process(es); not killing them`, {
|
|
@@ -143066,17 +143415,29 @@ async function freeTcpPortIfMoxxy(port, logger) {
|
|
|
143066
143415
|
});
|
|
143067
143416
|
return false;
|
|
143068
143417
|
}
|
|
143418
|
+
let attempted = false;
|
|
143069
143419
|
for (const { pid } of holders) {
|
|
143420
|
+
if (!looksLikeMoxxy(await deps.pidCommand(pid)))
|
|
143421
|
+
continue;
|
|
143070
143422
|
try {
|
|
143071
|
-
|
|
143423
|
+
deps.kill(pid, "SIGTERM");
|
|
143424
|
+
attempted = true;
|
|
143072
143425
|
} catch {
|
|
143073
143426
|
}
|
|
143074
143427
|
}
|
|
143075
|
-
|
|
143428
|
+
if (!attempted)
|
|
143429
|
+
return false;
|
|
143430
|
+
await new Promise((r2) => setTimeout(r2, deps.graceMs ?? 400));
|
|
143076
143431
|
for (const { pid } of holders) {
|
|
143077
143432
|
try {
|
|
143078
|
-
|
|
143079
|
-
|
|
143433
|
+
deps.kill(pid, 0);
|
|
143434
|
+
} catch {
|
|
143435
|
+
continue;
|
|
143436
|
+
}
|
|
143437
|
+
if (!looksLikeMoxxy(await deps.pidCommand(pid)))
|
|
143438
|
+
continue;
|
|
143439
|
+
try {
|
|
143440
|
+
deps.kill(pid, "SIGKILL");
|
|
143080
143441
|
} catch {
|
|
143081
143442
|
}
|
|
143082
143443
|
}
|
|
@@ -143634,6 +143995,7 @@ async function createWebSocketTransportServer(opts) {
|
|
|
143634
143995
|
let currentToken = opts.authToken;
|
|
143635
143996
|
let currentAllowedOrigins = opts.allowedOrigins ?? [];
|
|
143636
143997
|
let connections = 0;
|
|
143998
|
+
let pending2 = 0;
|
|
143637
143999
|
const wss = new import_websocket_server.default({
|
|
143638
144000
|
host,
|
|
143639
144001
|
port: opts.port,
|
|
@@ -143643,11 +144005,14 @@ async function createWebSocketTransportServer(opts) {
|
|
|
143643
144005
|
console.warn(`[moxxy] ws bridge: rejected browser-origin upgrade (Origin: ${String(info.req.headers.origin)})`);
|
|
143644
144006
|
return false;
|
|
143645
144007
|
}
|
|
143646
|
-
if (connections >= maxConnections) {
|
|
144008
|
+
if (connections + pending2 >= maxConnections) {
|
|
143647
144009
|
console.warn(`[moxxy] ws bridge: rejected upgrade \u2014 connection cap (${maxConnections}) reached`);
|
|
143648
144010
|
return false;
|
|
143649
144011
|
}
|
|
143650
|
-
|
|
144012
|
+
const ok = checkWsAuth(info.req, currentToken, { allowQueryToken: opts.allowQueryToken });
|
|
144013
|
+
if (ok)
|
|
144014
|
+
pending2 += 1;
|
|
144015
|
+
return ok;
|
|
143651
144016
|
},
|
|
143652
144017
|
// When the client offers subprotocols (the moxxy.bearer.* convention),
|
|
143653
144018
|
// select the moxxy protocol WITHOUT echoing the token-bearing entry back.
|
|
@@ -143655,6 +144020,8 @@ async function createWebSocketTransportServer(opts) {
|
|
|
143655
144020
|
});
|
|
143656
144021
|
const connectionHandlers = [];
|
|
143657
144022
|
wss.on("connection", (ws) => {
|
|
144023
|
+
if (pending2 > 0)
|
|
144024
|
+
pending2 -= 1;
|
|
143658
144025
|
connections += 1;
|
|
143659
144026
|
ws.once("close", () => {
|
|
143660
144027
|
connections -= 1;
|
|
@@ -143762,6 +144129,7 @@ var optionalWorkspace = z.string().min(1).max(256).optional();
|
|
|
143762
144129
|
var MAX_AUDIO_BASE64 = 4e7;
|
|
143763
144130
|
var MAX_INLINE_ATTACHMENT_CONTENT = 12e6;
|
|
143764
144131
|
var commandName = z.string().min(1).max(64).regex(/^[A-Za-z0-9][A-Za-z0-9._-]*$/, "invalid command name");
|
|
144132
|
+
var appId = z.string().min(1).max(64).regex(/^[a-z][a-z0-9-]*$/, "invalid app id");
|
|
143765
144133
|
var workflowName = z.string().min(1).max(200).refine((s2) => !s2.includes("..") && !s2.includes("/") && !s2.includes("\\"), "invalid workflow name");
|
|
143766
144134
|
var ipcInputSchemas = {
|
|
143767
144135
|
// No-arg, but spawns a child process (npm install) — pin the payload to
|
|
@@ -143871,6 +144239,20 @@ var ipcInputSchemas = {
|
|
|
143871
144239
|
"settings.writeSkill": z.object({ name: skillName, body: z.string().max(1e6) }),
|
|
143872
144240
|
"settings.readSkill": z.object({ name: skillName }),
|
|
143873
144241
|
"settings.deleteSkill": z.object({ name: skillName }),
|
|
144242
|
+
// Desktop apps: appId keys the per-app install dir + a network download, so
|
|
144243
|
+
// pin it to a non-traversing slug. (pickDocument is no-arg → see below.)
|
|
144244
|
+
"apps.status": z.object({ appId }),
|
|
144245
|
+
"apps.install": z.object({ appId }),
|
|
144246
|
+
"apps.uninstall": z.object({ appId }),
|
|
144247
|
+
// Anonymizer: parseDocument reads a file (bound the path), saveRedacted writes
|
|
144248
|
+
// one (bound name + cap content so a renderer can't OOM main). pickDocument
|
|
144249
|
+
// takes nothing — pin it to "nothing" so no args can be smuggled across.
|
|
144250
|
+
"anonymizer.pickDocument": z.undefined(),
|
|
144251
|
+
"anonymizer.parseDocument": z.object({ path: z.string().min(1).max(4096) }),
|
|
144252
|
+
"anonymizer.saveRedacted": z.object({
|
|
144253
|
+
suggestedName: z.string().min(1).max(255),
|
|
144254
|
+
content: z.string().max(2e7)
|
|
144255
|
+
}),
|
|
143874
144256
|
"desks.create": z.object({ name: z.string().min(1).max(200), cwd: z.string().min(1).max(4096) }),
|
|
143875
144257
|
// Mirror desks.create's name bounds — rename writes the name into the desks
|
|
143876
144258
|
// JSON, so an unbounded string would let a renderer bloat the state file.
|
|
@@ -144105,6 +144487,7 @@ var MobileSessionHost = class {
|
|
|
144105
144487
|
this.bus.handle("session.newSession", async () => {
|
|
144106
144488
|
for (const controller of this.turns.values())
|
|
144107
144489
|
controller.abort();
|
|
144490
|
+
this.autoApprove = false;
|
|
144108
144491
|
if (this.session.reset)
|
|
144109
144492
|
await this.session.reset();
|
|
144110
144493
|
else
|
|
@@ -144251,6 +144634,8 @@ var MobileSessionHost = class {
|
|
|
144251
144634
|
return { turnId };
|
|
144252
144635
|
}
|
|
144253
144636
|
openAsk(req) {
|
|
144637
|
+
if (this.disposed)
|
|
144638
|
+
return Promise.resolve({ mode: "deny" });
|
|
144254
144639
|
const requestId = `ask-${++this.askCounter}`;
|
|
144255
144640
|
return new Promise((resolve12) => {
|
|
144256
144641
|
this.pendingAsks.set(requestId, resolve12);
|
|
@@ -144562,7 +144947,7 @@ function htmlToMarkdown(html, opts = {}) {
|
|
|
144562
144947
|
${"#".repeat(Number(lvl))} ${stripTags(inner).trim()}
|
|
144563
144948
|
|
|
144564
144949
|
`);
|
|
144565
|
-
body = body.replace(/<a\b[^>]*\bhref="([^"]+)"[^>]*>([\s\S]*?)<\/a>/gi, (_2,
|
|
144950
|
+
body = body.replace(/<a\b[^>]*\bhref=(?:"([^"]+)"|'([^']+)'|([^\s>]+))[^>]*>([\s\S]*?)<\/a>/gi, (_2, dq, sq, uq, inner) => `[${stripTags(inner).trim()}](${dq ?? sq ?? uq})`);
|
|
144566
144951
|
body = body.replace(/<pre\b[^>]*>([\s\S]*?)<\/pre>/gi, (_2, inner) => `
|
|
144567
144952
|
|
|
144568
144953
|
\`\`\`
|
|
@@ -145479,6 +145864,8 @@ var TerminalProcessImpl = class {
|
|
|
145479
145864
|
child.stderr.on("data", (b3) => this.emitData(b3.toString("utf8")));
|
|
145480
145865
|
child.on("exit", (code) => this.emitExit(code ?? 0));
|
|
145481
145866
|
child.on("error", () => this.emitExit(1));
|
|
145867
|
+
child.stdin.on("error", () => {
|
|
145868
|
+
});
|
|
145482
145869
|
}
|
|
145483
145870
|
}
|
|
145484
145871
|
emitData(d2) {
|
|
@@ -145518,10 +145905,13 @@ var TerminalProcessImpl = class {
|
|
|
145518
145905
|
write(data) {
|
|
145519
145906
|
if (!this.alive)
|
|
145520
145907
|
return;
|
|
145521
|
-
|
|
145522
|
-
this.pty
|
|
145523
|
-
|
|
145524
|
-
|
|
145908
|
+
try {
|
|
145909
|
+
if (this.pty)
|
|
145910
|
+
this.pty.write(data);
|
|
145911
|
+
else
|
|
145912
|
+
this.child?.stdin.write(data);
|
|
145913
|
+
} catch {
|
|
145914
|
+
}
|
|
145525
145915
|
}
|
|
145526
145916
|
resize(cols, rows) {
|
|
145527
145917
|
if (this.pty && this.alive) {
|
|
@@ -146137,16 +146527,16 @@ function buildProviderAdminPlugin(opts) {
|
|
|
146137
146527
|
// ../plugin-usage-stats/dist/index.js
|
|
146138
146528
|
init_dist();
|
|
146139
146529
|
function buildUsageStatsPlugin(opts = {}) {
|
|
146140
|
-
let
|
|
146530
|
+
let initMaxSeq = null;
|
|
146141
146531
|
return definePlugin({
|
|
146142
146532
|
name: "@moxxy/plugin-usage-stats",
|
|
146143
146533
|
version: "0.0.0",
|
|
146144
146534
|
hooks: {
|
|
146145
146535
|
onInit(ctx) {
|
|
146146
|
-
|
|
146536
|
+
initMaxSeq = maxSeq(ctx.log.slice());
|
|
146147
146537
|
},
|
|
146148
146538
|
async onShutdown(ctx) {
|
|
146149
|
-
const live = ctx.log.slice(
|
|
146539
|
+
const live = initMaxSeq === null ? ctx.log.slice() : ctx.log.slice().filter((e3) => e3.seq > initMaxSeq);
|
|
146150
146540
|
if (live.length === 0)
|
|
146151
146541
|
return;
|
|
146152
146542
|
const delta = summarizeTokensByModel(live);
|
|
@@ -146155,6 +146545,13 @@ function buildUsageStatsPlugin(opts = {}) {
|
|
|
146155
146545
|
}
|
|
146156
146546
|
});
|
|
146157
146547
|
}
|
|
146548
|
+
function maxSeq(events) {
|
|
146549
|
+
let max = null;
|
|
146550
|
+
for (const e3 of events)
|
|
146551
|
+
if (max === null || e3.seq > max)
|
|
146552
|
+
max = e3.seq;
|
|
146553
|
+
return max;
|
|
146554
|
+
}
|
|
146158
146555
|
var infoCmd = {
|
|
146159
146556
|
name: "info",
|
|
146160
146557
|
description: "Show provider \xB7 model \xB7 mode \xB7 plugin/skill counts",
|
|
@@ -146247,36 +146644,25 @@ async function compactSession(session) {
|
|
|
146247
146644
|
if (events.length === 0) {
|
|
146248
146645
|
return { kind: "text", text: "nothing to compact: event log is empty" };
|
|
146249
146646
|
}
|
|
146250
|
-
const providerCtxWindow = resolveActiveContextWindow(s2);
|
|
146251
146647
|
const provider = safe(() => s2.providers?.getActive()) ?? void 0;
|
|
146252
146648
|
const model = provider?.models[0]?.id;
|
|
146649
|
+
const contextWindow = provider?.models[0]?.contextWindow;
|
|
146253
146650
|
try {
|
|
146254
|
-
const result = await
|
|
146255
|
-
|
|
146256
|
-
|
|
146257
|
-
|
|
146258
|
-
estimatedTokens: estimateContextTokens$1(s2.log.asReader ? s2.log.asReader() : s2.log),
|
|
146259
|
-
reserveForOutput: 0
|
|
146260
|
-
},
|
|
146261
|
-
signal: s2.signal ?? new AbortController().signal,
|
|
146651
|
+
const result = await runManualCompaction({
|
|
146652
|
+
compactor,
|
|
146653
|
+
log: s2.log,
|
|
146654
|
+
signal: s2.signal,
|
|
146262
146655
|
...provider ? { provider } : {},
|
|
146263
|
-
...model ? { model } : {}
|
|
146656
|
+
...model !== void 0 ? { model } : {},
|
|
146657
|
+
...contextWindow !== void 0 ? { contextWindow } : {},
|
|
146658
|
+
...s2.id !== void 0 ? { sessionId: s2.id } : {}
|
|
146264
146659
|
});
|
|
146265
|
-
if (result.
|
|
146660
|
+
if (!result.compacted) {
|
|
146266
146661
|
return { kind: "text", text: "nothing to compact yet" };
|
|
146267
146662
|
}
|
|
146268
|
-
const lastEvent = events[events.length - 1];
|
|
146269
|
-
const emittable = {
|
|
146270
|
-
sessionId: s2.id ?? lastEvent?.sessionId,
|
|
146271
|
-
turnId: lastEvent?.turnId,
|
|
146272
|
-
source: "compactor",
|
|
146273
|
-
...result
|
|
146274
|
-
};
|
|
146275
|
-
await s2.log.append(emittable);
|
|
146276
|
-
const compactedEvents = result.replacedRange[1] - result.replacedRange[0] + 1;
|
|
146277
146663
|
return {
|
|
146278
146664
|
kind: "text",
|
|
146279
|
-
text: `context compacted: ${formatCount2(
|
|
146665
|
+
text: `context compacted: ${formatCount2(result.eventsCompacted)} ${plural2(result.eventsCompacted, "event")}, ~${formatTokenCount2(result.tokensSaved)} tokens saved`
|
|
146280
146666
|
};
|
|
146281
146667
|
} catch (err) {
|
|
146282
146668
|
return {
|
|
@@ -146285,17 +146671,6 @@ async function compactSession(session) {
|
|
|
146285
146671
|
};
|
|
146286
146672
|
}
|
|
146287
146673
|
}
|
|
146288
|
-
function resolveActiveContextWindow(s2) {
|
|
146289
|
-
try {
|
|
146290
|
-
const provider = s2.providers?.getActive();
|
|
146291
|
-
if (!provider)
|
|
146292
|
-
return Number.MAX_SAFE_INTEGER;
|
|
146293
|
-
const window2 = provider.models[0]?.contextWindow;
|
|
146294
|
-
return window2 && window2 > 0 ? window2 : Number.MAX_SAFE_INTEGER;
|
|
146295
|
-
} catch {
|
|
146296
|
-
return Number.MAX_SAFE_INTEGER;
|
|
146297
|
-
}
|
|
146298
|
-
}
|
|
146299
146674
|
function formatCount2(value) {
|
|
146300
146675
|
return new Intl.NumberFormat("en-US").format(value);
|
|
146301
146676
|
}
|
|
@@ -146348,15 +146723,26 @@ function buildViewPlugin(opts) {
|
|
|
146348
146723
|
});
|
|
146349
146724
|
}
|
|
146350
146725
|
var IS_DARWIN = process.platform === "darwin";
|
|
146726
|
+
function procFailureCause(proc, timeoutMs) {
|
|
146727
|
+
if (proc.timedOut) {
|
|
146728
|
+
return timeoutMs ? `timed out after ${timeoutMs}ms` : "timed out";
|
|
146729
|
+
}
|
|
146730
|
+
if (proc.aborted)
|
|
146731
|
+
return "aborted (turn cancelled)";
|
|
146732
|
+
return "";
|
|
146733
|
+
}
|
|
146351
146734
|
function runProcess2(cmd, args, opts = {}) {
|
|
146352
146735
|
return new Promise((resolve12, reject) => {
|
|
146353
146736
|
const child = spawn(cmd, [...args], { stdio: ["pipe", "pipe", "pipe"] });
|
|
146354
146737
|
const stdoutChunks = [];
|
|
146355
146738
|
let stderr = "";
|
|
146356
146739
|
let settled = false;
|
|
146740
|
+
let timedOut = false;
|
|
146741
|
+
let aborted = false;
|
|
146357
146742
|
const onAbort = () => {
|
|
146358
146743
|
if (settled)
|
|
146359
146744
|
return;
|
|
146745
|
+
aborted = true;
|
|
146360
146746
|
try {
|
|
146361
146747
|
child.kill("SIGTERM");
|
|
146362
146748
|
} catch {
|
|
@@ -146366,6 +146752,7 @@ function runProcess2(cmd, args, opts = {}) {
|
|
|
146366
146752
|
const timer = opts.timeoutMs ? setTimeout(() => {
|
|
146367
146753
|
if (settled)
|
|
146368
146754
|
return;
|
|
146755
|
+
timedOut = true;
|
|
146369
146756
|
try {
|
|
146370
146757
|
child.kill("SIGTERM");
|
|
146371
146758
|
} catch {
|
|
@@ -146396,7 +146783,9 @@ function runProcess2(cmd, args, opts = {}) {
|
|
|
146396
146783
|
resolve12({
|
|
146397
146784
|
exitCode: code ?? -1,
|
|
146398
146785
|
stdout: Buffer.concat(stdoutChunks).toString("utf8"),
|
|
146399
|
-
stderr
|
|
146786
|
+
stderr,
|
|
146787
|
+
timedOut,
|
|
146788
|
+
aborted
|
|
146400
146789
|
});
|
|
146401
146790
|
});
|
|
146402
146791
|
if (opts.input !== void 0) {
|
|
@@ -146429,10 +146818,11 @@ var applescriptTool = defineTool({
|
|
|
146429
146818
|
timeoutMs: 3e4
|
|
146430
146819
|
});
|
|
146431
146820
|
if (proc.exitCode !== 0) {
|
|
146821
|
+
const cause = procFailureCause(proc, 3e4);
|
|
146432
146822
|
throw new MoxxyError({
|
|
146433
146823
|
code: "TOOL_ERROR",
|
|
146434
|
-
message: `osascript failed (exit ${proc.exitCode}): ${proc.stderr.trim() || "(no error message)"}`,
|
|
146435
|
-
context: { tool: "computer_applescript", exitCode: proc.exitCode }
|
|
146824
|
+
message: cause ? `osascript ${cause}` : `osascript failed (exit ${proc.exitCode}): ${proc.stderr.trim() || "(no error message)"}`,
|
|
146825
|
+
context: { tool: "computer_applescript", exitCode: proc.exitCode, timedOut: proc.timedOut ? 1 : 0 }
|
|
146436
146826
|
});
|
|
146437
146827
|
}
|
|
146438
146828
|
return { ok: true, output: proc.stdout.trim() };
|
|
@@ -146460,10 +146850,11 @@ end tell`;
|
|
|
146460
146850
|
timeoutMs: 1e4
|
|
146461
146851
|
});
|
|
146462
146852
|
if (proc.exitCode !== 0) {
|
|
146853
|
+
const cause = procFailureCause(proc, 1e4);
|
|
146463
146854
|
throw new MoxxyError({
|
|
146464
146855
|
code: "TOOL_ERROR",
|
|
146465
|
-
message: `click failed (exit ${proc.exitCode}): ${proc.stderr.trim() || "(check Accessibility permission in System Settings \u2192 Privacy & Security \u2192 Accessibility)"}`,
|
|
146466
|
-
context: { tool: "computer_click", exitCode: proc.exitCode }
|
|
146856
|
+
message: cause ? `click ${cause}` : `click failed (exit ${proc.exitCode}): ${proc.stderr.trim() || "(check Accessibility permission in System Settings \u2192 Privacy & Security \u2192 Accessibility)"}`,
|
|
146857
|
+
context: { tool: "computer_click", exitCode: proc.exitCode, timedOut: proc.timedOut ? 1 : 0 }
|
|
146467
146858
|
});
|
|
146468
146859
|
}
|
|
146469
146860
|
return { ok: true, x: x4, y: y2, count: n2 };
|
|
@@ -146485,10 +146876,11 @@ var clipboardTool = defineTool({
|
|
|
146485
146876
|
timeoutMs: 5e3
|
|
146486
146877
|
});
|
|
146487
146878
|
if (proc2.exitCode !== 0) {
|
|
146879
|
+
const cause = procFailureCause(proc2, 5e3);
|
|
146488
146880
|
throw new MoxxyError({
|
|
146489
146881
|
code: "TOOL_ERROR",
|
|
146490
|
-
message: `pbpaste failed (exit ${proc2.exitCode}): ${proc2.stderr.trim()}`,
|
|
146491
|
-
context: { tool: "computer_clipboard", exitCode: proc2.exitCode }
|
|
146882
|
+
message: cause ? `pbpaste ${cause}` : `pbpaste failed (exit ${proc2.exitCode}): ${proc2.stderr.trim()}`,
|
|
146883
|
+
context: { tool: "computer_clipboard", exitCode: proc2.exitCode, timedOut: proc2.timedOut ? 1 : 0 }
|
|
146492
146884
|
});
|
|
146493
146885
|
}
|
|
146494
146886
|
return { ok: true, text: proc2.stdout };
|
|
@@ -146506,10 +146898,11 @@ var clipboardTool = defineTool({
|
|
|
146506
146898
|
timeoutMs: 5e3
|
|
146507
146899
|
});
|
|
146508
146900
|
if (proc.exitCode !== 0) {
|
|
146901
|
+
const cause = procFailureCause(proc, 5e3);
|
|
146509
146902
|
throw new MoxxyError({
|
|
146510
146903
|
code: "TOOL_ERROR",
|
|
146511
|
-
message: `pbcopy failed (exit ${proc.exitCode}): ${proc.stderr.trim()}`,
|
|
146512
|
-
context: { tool: "computer_clipboard", exitCode: proc.exitCode }
|
|
146904
|
+
message: cause ? `pbcopy ${cause}` : `pbcopy failed (exit ${proc.exitCode}): ${proc.stderr.trim()}`,
|
|
146905
|
+
context: { tool: "computer_clipboard", exitCode: proc.exitCode, timedOut: proc.timedOut ? 1 : 0 }
|
|
146513
146906
|
});
|
|
146514
146907
|
}
|
|
146515
146908
|
return { ok: true, length: text.length };
|
|
@@ -146547,6 +146940,16 @@ var KEY_CODES = {
|
|
|
146547
146940
|
f11: 103,
|
|
146548
146941
|
f12: 111
|
|
146549
146942
|
};
|
|
146943
|
+
var WHITESPACE_KEY_CODES = {
|
|
146944
|
+
" ": 49,
|
|
146945
|
+
// space
|
|
146946
|
+
" ": 48,
|
|
146947
|
+
// tab
|
|
146948
|
+
"\r": 36,
|
|
146949
|
+
// return
|
|
146950
|
+
"\n": 36
|
|
146951
|
+
// return
|
|
146952
|
+
};
|
|
146550
146953
|
var keyTool = defineTool({
|
|
146551
146954
|
name: "computer_key",
|
|
146552
146955
|
description: "Send a single key chord with optional modifiers. Use this for shortcuts (cmd+c, cmd+tab, cmd+shift+4) and named keys (return, tab, escape, arrows, page_up/down, f1\u2013f12). For typing arbitrary text, use computer_type.",
|
|
@@ -146564,10 +146967,11 @@ var keyTool = defineTool({
|
|
|
146564
146967
|
timeoutMs: 1e4
|
|
146565
146968
|
});
|
|
146566
146969
|
if (proc.exitCode !== 0) {
|
|
146970
|
+
const cause = procFailureCause(proc, 1e4);
|
|
146567
146971
|
throw new MoxxyError({
|
|
146568
146972
|
code: "TOOL_ERROR",
|
|
146569
|
-
message: `key failed (exit ${proc.exitCode}): ${proc.stderr.trim() || "(check Accessibility permission)"}`,
|
|
146570
|
-
context: { tool: "computer_key", exitCode: proc.exitCode }
|
|
146973
|
+
message: cause ? `key ${cause}` : `key failed (exit ${proc.exitCode}): ${proc.stderr.trim() || "(check Accessibility permission)"}`,
|
|
146974
|
+
context: { tool: "computer_key", exitCode: proc.exitCode, timedOut: proc.timedOut ? 1 : 0 }
|
|
146571
146975
|
});
|
|
146572
146976
|
}
|
|
146573
146977
|
return { ok: true, key, modifiers: mods };
|
|
@@ -146579,6 +146983,12 @@ function buildKeyScript(key, modifiers) {
|
|
|
146579
146983
|
if (lower2 in KEY_CODES) {
|
|
146580
146984
|
return `tell application "System Events" to key code ${KEY_CODES[lower2]}${usingClause}`;
|
|
146581
146985
|
}
|
|
146986
|
+
if (key.length === 1) {
|
|
146987
|
+
const wsCode = WHITESPACE_KEY_CODES[key];
|
|
146988
|
+
if (wsCode !== void 0) {
|
|
146989
|
+
return `tell application "System Events" to key code ${wsCode}${usingClause}`;
|
|
146990
|
+
}
|
|
146991
|
+
}
|
|
146582
146992
|
if (key.length === 1) {
|
|
146583
146993
|
const literal3 = `"${key.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
146584
146994
|
return `tell application "System Events" to keystroke ${literal3}${usingClause}`;
|
|
@@ -146630,10 +147040,11 @@ var openTool = defineTool({
|
|
|
146630
147040
|
timeoutMs: 1e4
|
|
146631
147041
|
});
|
|
146632
147042
|
if (proc.exitCode !== 0) {
|
|
147043
|
+
const cause = procFailureCause(proc, 1e4);
|
|
146633
147044
|
throw new MoxxyError({
|
|
146634
147045
|
code: "TOOL_ERROR",
|
|
146635
|
-
message: `open failed (exit ${proc.exitCode}): ${proc.stderr.trim() || "(no error message)"}`,
|
|
146636
|
-
context: { tool: "computer_open", exitCode: proc.exitCode }
|
|
147046
|
+
message: cause ? `open ${cause}` : `open failed (exit ${proc.exitCode}): ${proc.stderr.trim() || "(no error message)"}`,
|
|
147047
|
+
context: { tool: "computer_open", exitCode: proc.exitCode, timedOut: proc.timedOut ? 1 : 0 }
|
|
146637
147048
|
});
|
|
146638
147049
|
}
|
|
146639
147050
|
return { ok: true, app, target };
|
|
@@ -146664,7 +147075,8 @@ var screenshotTool = defineTool({
|
|
|
146664
147075
|
const fmt2 = format ?? DEFAULT_FORMAT;
|
|
146665
147076
|
const dim3 = maxDim ?? DEFAULT_MAX_DIM;
|
|
146666
147077
|
const q3 = quality ?? DEFAULT_JPEG_QUALITY;
|
|
146667
|
-
const
|
|
147078
|
+
const uniq = randomUUID();
|
|
147079
|
+
const captureTmp = path3.join(os5.tmpdir(), `moxxy-screencap-${process.pid}-${Date.now()}-${uniq}.png`);
|
|
146668
147080
|
const captureArgs = ["-x", "-t", "png"];
|
|
146669
147081
|
if (region) {
|
|
146670
147082
|
captureArgs.push("-R", `${region.x},${region.y},${region.width},${region.height}`);
|
|
@@ -146675,14 +147087,15 @@ var screenshotTool = defineTool({
|
|
|
146675
147087
|
timeoutMs: 15e3
|
|
146676
147088
|
});
|
|
146677
147089
|
if (cap.exitCode !== 0) {
|
|
147090
|
+
const cause = procFailureCause(cap, 15e3);
|
|
146678
147091
|
throw new MoxxyError({
|
|
146679
147092
|
code: "TOOL_ERROR",
|
|
146680
|
-
message: `screencapture failed (exit ${cap.exitCode}): ${cap.stderr.trim() || "(no stderr \u2014 likely Screen Recording permission missing \u2014 grant in System Settings \u2192 Privacy & Security)"}`,
|
|
146681
|
-
context: { tool: "computer_screenshot", exitCode: cap.exitCode }
|
|
147093
|
+
message: cause ? `screencapture ${cause}` : `screencapture failed (exit ${cap.exitCode}): ${cap.stderr.trim() || "(no stderr \u2014 likely Screen Recording permission missing \u2014 grant in System Settings \u2192 Privacy & Security)"}`,
|
|
147094
|
+
context: { tool: "computer_screenshot", exitCode: cap.exitCode, timedOut: cap.timedOut ? 1 : 0 }
|
|
146682
147095
|
});
|
|
146683
147096
|
}
|
|
146684
147097
|
const outExt = fmt2 === "jpeg" ? "jpg" : "png";
|
|
146685
|
-
const outTmp = path3.join(os5.tmpdir(), `moxxy-screencap-${process.pid}-${Date.now()}-out.${outExt}`);
|
|
147098
|
+
const outTmp = path3.join(os5.tmpdir(), `moxxy-screencap-${process.pid}-${Date.now()}-${uniq}-out.${outExt}`);
|
|
146686
147099
|
const sipsArgs = [
|
|
146687
147100
|
"-Z",
|
|
146688
147101
|
String(dim3),
|
|
@@ -146701,10 +147114,11 @@ var screenshotTool = defineTool({
|
|
|
146701
147114
|
await promises.rm(captureTmp, { force: true });
|
|
146702
147115
|
if (sip.exitCode !== 0) {
|
|
146703
147116
|
await promises.rm(outTmp, { force: true });
|
|
147117
|
+
const cause = procFailureCause(sip, 15e3);
|
|
146704
147118
|
throw new MoxxyError({
|
|
146705
147119
|
code: "TOOL_ERROR",
|
|
146706
|
-
message: `sips resize/convert failed (exit ${sip.exitCode}): ${sip.stderr.trim() || "(no error message)"}`,
|
|
146707
|
-
context: { tool: "computer_screenshot", exitCode: sip.exitCode }
|
|
147120
|
+
message: cause ? `sips resize/convert ${cause}` : `sips resize/convert failed (exit ${sip.exitCode}): ${sip.stderr.trim() || "(no error message)"}`,
|
|
147121
|
+
context: { tool: "computer_screenshot", exitCode: sip.exitCode, timedOut: sip.timedOut ? 1 : 0 }
|
|
146708
147122
|
});
|
|
146709
147123
|
}
|
|
146710
147124
|
try {
|
|
@@ -146747,10 +147161,11 @@ var typeTool = defineTool({
|
|
|
146747
147161
|
timeoutMs: 3e4
|
|
146748
147162
|
});
|
|
146749
147163
|
if (proc.exitCode !== 0) {
|
|
147164
|
+
const cause = procFailureCause(proc, 3e4);
|
|
146750
147165
|
throw new MoxxyError({
|
|
146751
147166
|
code: "TOOL_ERROR",
|
|
146752
|
-
message: `type failed (exit ${proc.exitCode}): ${proc.stderr.trim() || "(check Accessibility permission)"}`,
|
|
146753
|
-
context: { tool: "computer_type", exitCode: proc.exitCode }
|
|
147167
|
+
message: cause ? `type ${cause}` : `type failed (exit ${proc.exitCode}): ${proc.stderr.trim() || "(check Accessibility permission)"}`,
|
|
147168
|
+
context: { tool: "computer_type", exitCode: proc.exitCode, timedOut: proc.timedOut ? 1 : 0 }
|
|
146754
147169
|
});
|
|
146755
147170
|
}
|
|
146756
147171
|
return { ok: true, length: text.length };
|
|
@@ -147254,6 +147669,8 @@ var stepSchema = z.object({
|
|
|
147254
147669
|
needs: z.array(z.string().min(1)).default([]),
|
|
147255
147670
|
when: z.string().min(1).optional(),
|
|
147256
147671
|
onError: z.enum(["fail", "continue", "retry"]).default("fail"),
|
|
147672
|
+
// `retries` only takes effect when `onError: 'retry'`; with 'fail'/'continue'
|
|
147673
|
+
// the step runs exactly one attempt (see runStep in executor/steps.ts).
|
|
147257
147674
|
retries: z.number().int().min(0).max(3).default(0),
|
|
147258
147675
|
label: z.string().max(60).optional(),
|
|
147259
147676
|
format: z.enum(["json", "plain"]).optional(),
|
|
@@ -147682,7 +148099,16 @@ async function loadDir2(dir, scope, logger) {
|
|
|
147682
148099
|
if (!entry.isFile() || !/\.ya?ml$/i.test(entry.name))
|
|
147683
148100
|
continue;
|
|
147684
148101
|
const full = path3.join(dir, entry.name);
|
|
147685
|
-
|
|
148102
|
+
let raw;
|
|
148103
|
+
try {
|
|
148104
|
+
raw = await promises.readFile(full, "utf8");
|
|
148105
|
+
} catch (err) {
|
|
148106
|
+
logger?.warn?.("workflow: unreadable file, skipping", {
|
|
148107
|
+
path: full,
|
|
148108
|
+
error: err instanceof Error ? err.message : String(err)
|
|
148109
|
+
});
|
|
148110
|
+
continue;
|
|
148111
|
+
}
|
|
147686
148112
|
const result = parseWorkflowYaml(raw);
|
|
147687
148113
|
if (!result.ok || !result.workflow) {
|
|
147688
148114
|
logger?.warn?.("workflow: invalid file, skipping", { path: full, errors: result.errors });
|
|
@@ -147719,6 +148145,12 @@ var WorkflowStore = class {
|
|
|
147719
148145
|
byName = /* @__PURE__ */ new Map();
|
|
147720
148146
|
opts;
|
|
147721
148147
|
loaded = false;
|
|
148148
|
+
// Per-instance lock (invariant: serialize whole-map RMW). A reload
|
|
148149
|
+
// (`load()` clears then refills byName) must never interleave with a save
|
|
148150
|
+
// (read byName → write file → set byName) — e.g. an autonomous onChanged
|
|
148151
|
+
// re-sync racing a user `/workflows enable`, or two concurrent toggles —
|
|
148152
|
+
// or the in-memory registry briefly desyncs from disk.
|
|
148153
|
+
mutex = createMutex();
|
|
147722
148154
|
constructor(opts) {
|
|
147723
148155
|
this.opts = opts;
|
|
147724
148156
|
}
|
|
@@ -147730,6 +148162,14 @@ var WorkflowStore = class {
|
|
|
147730
148162
|
}
|
|
147731
148163
|
/** (Re)scan all sources and rebuild the in-memory map. */
|
|
147732
148164
|
async load() {
|
|
148165
|
+
await this.mutex.run(() => this.loadUnlocked());
|
|
148166
|
+
}
|
|
148167
|
+
/**
|
|
148168
|
+
* Rebuild the map without acquiring the mutex — only call from a context
|
|
148169
|
+
* that already holds it (mutators below), so the clear+refill is atomic
|
|
148170
|
+
* with respect to other mutators.
|
|
148171
|
+
*/
|
|
148172
|
+
async loadUnlocked() {
|
|
147733
148173
|
const discovered = await discoverWorkflows({
|
|
147734
148174
|
userDir: this.userDir(),
|
|
147735
148175
|
projectDir: this.projectDir(),
|
|
@@ -147742,6 +148182,10 @@ var WorkflowStore = class {
|
|
|
147742
148182
|
this.byName.set(wf.workflow.name, wf);
|
|
147743
148183
|
this.loaded = true;
|
|
147744
148184
|
}
|
|
148185
|
+
async ensureLoadedUnlocked() {
|
|
148186
|
+
if (!this.loaded)
|
|
148187
|
+
await this.loadUnlocked();
|
|
148188
|
+
}
|
|
147745
148189
|
async ensureLoaded() {
|
|
147746
148190
|
if (!this.loaded)
|
|
147747
148191
|
await this.load();
|
|
@@ -147760,17 +148204,19 @@ var WorkflowStore = class {
|
|
|
147760
148204
|
}
|
|
147761
148205
|
/** Write a new workflow file and register it. Rejects duplicate names. */
|
|
147762
148206
|
async create(workflow, scope) {
|
|
147763
|
-
|
|
147764
|
-
|
|
147765
|
-
|
|
147766
|
-
|
|
147767
|
-
|
|
147768
|
-
|
|
147769
|
-
|
|
147770
|
-
|
|
147771
|
-
|
|
147772
|
-
|
|
147773
|
-
|
|
148207
|
+
return this.mutex.run(async () => {
|
|
148208
|
+
await this.ensureLoadedUnlocked();
|
|
148209
|
+
if (this.byName.has(workflow.name)) {
|
|
148210
|
+
throw new Error(`workflow "${workflow.name}" already exists \u2014 use update instead`);
|
|
148211
|
+
}
|
|
148212
|
+
const dir = scope === "project" ? this.projectDir() : this.userDir();
|
|
148213
|
+
await promises.mkdir(dir, { recursive: true });
|
|
148214
|
+
const file = await uniqueFilename2(dir, workflow.name);
|
|
148215
|
+
await writeFileAtomic(file, serializeWorkflow(workflow));
|
|
148216
|
+
const entry = { workflow, path: file, scope };
|
|
148217
|
+
this.byName.set(workflow.name, entry);
|
|
148218
|
+
return entry;
|
|
148219
|
+
});
|
|
147774
148220
|
}
|
|
147775
148221
|
/**
|
|
147776
148222
|
* Replace a workflow with a new definition. In-place rewrite for user/project
|
|
@@ -147781,7 +148227,10 @@ var WorkflowStore = class {
|
|
|
147781
148227
|
* rename doesn't leave an orphaned duplicate file + stale entry behind.
|
|
147782
148228
|
*/
|
|
147783
148229
|
async save(workflow, previousName) {
|
|
147784
|
-
|
|
148230
|
+
return this.mutex.run(() => this.saveUnlocked(workflow, previousName));
|
|
148231
|
+
}
|
|
148232
|
+
async saveUnlocked(workflow, previousName) {
|
|
148233
|
+
await this.ensureLoadedUnlocked();
|
|
147785
148234
|
const renamed = previousName != null && previousName !== workflow.name ? this.byName.get(previousName) : void 0;
|
|
147786
148235
|
if (renamed && (renamed.scope === "user" || renamed.scope === "project")) {
|
|
147787
148236
|
await promises.rm(renamed.path, { force: true });
|
|
@@ -147806,24 +148255,28 @@ var WorkflowStore = class {
|
|
|
147806
148255
|
}
|
|
147807
148256
|
/** Toggle a workflow's `enabled` flag, persisting the change. */
|
|
147808
148257
|
async setEnabled(name, enabled) {
|
|
147809
|
-
|
|
147810
|
-
|
|
147811
|
-
|
|
147812
|
-
|
|
147813
|
-
|
|
148258
|
+
return this.mutex.run(async () => {
|
|
148259
|
+
await this.ensureLoadedUnlocked();
|
|
148260
|
+
const existing = this.byName.get(name);
|
|
148261
|
+
if (!existing)
|
|
148262
|
+
return null;
|
|
148263
|
+
return this.saveUnlocked({ ...existing.workflow, enabled });
|
|
148264
|
+
});
|
|
147814
148265
|
}
|
|
147815
148266
|
/** Delete a user/project workflow file. Read-only scopes cannot be deleted. */
|
|
147816
148267
|
async delete(name) {
|
|
147817
|
-
|
|
147818
|
-
|
|
147819
|
-
|
|
147820
|
-
|
|
147821
|
-
|
|
147822
|
-
|
|
147823
|
-
|
|
147824
|
-
|
|
147825
|
-
|
|
147826
|
-
|
|
148268
|
+
return this.mutex.run(async () => {
|
|
148269
|
+
await this.ensureLoadedUnlocked();
|
|
148270
|
+
const existing = this.byName.get(name);
|
|
148271
|
+
if (!existing)
|
|
148272
|
+
return { ok: false, reason: "not found" };
|
|
148273
|
+
if (existing.scope !== "user" && existing.scope !== "project") {
|
|
148274
|
+
return { ok: false, reason: `cannot delete a ${existing.scope} workflow` };
|
|
148275
|
+
}
|
|
148276
|
+
await promises.rm(existing.path, { force: true });
|
|
148277
|
+
this.byName.delete(name);
|
|
148278
|
+
return { ok: true };
|
|
148279
|
+
});
|
|
147827
148280
|
}
|
|
147828
148281
|
};
|
|
147829
148282
|
async function uniqueFilename2(dir, base2) {
|
|
@@ -148127,7 +148580,7 @@ function buildRunResult(ctx, status, ok, extra) {
|
|
|
148127
148580
|
};
|
|
148128
148581
|
}
|
|
148129
148582
|
async function runStep(step, scope, ctx) {
|
|
148130
|
-
const attempts = 1 + Math.max(0, step.retries);
|
|
148583
|
+
const attempts = step.onError === "retry" ? 1 + Math.max(0, step.retries) : 1;
|
|
148131
148584
|
let lastError = "";
|
|
148132
148585
|
for (let attempt = 0; attempt < attempts; attempt++) {
|
|
148133
148586
|
if (ctx.deps.signal.aborted)
|
|
@@ -148648,7 +149101,7 @@ ${userMessage.trim()}${FINALIZE_REPLY_SUFFIX}`;
|
|
|
148648
149101
|
var DAG_EXECUTOR_NAME = "dag";
|
|
148649
149102
|
var dagExecutor = defineWorkflowExecutor({
|
|
148650
149103
|
name: DAG_EXECUTOR_NAME,
|
|
148651
|
-
description: "DAG runner: steps with settled dependencies are scheduled in waves of up to `concurrency` ready steps, then executed sequentially within each wave (no overlap
|
|
149104
|
+
description: "DAG runner: steps with settled dependencies are scheduled in waves of up to `concurrency` ready steps, then executed sequentially within each wave (no overlap \u2014 `concurrency` caps the batch size drained per pass, not wall-clock latency).",
|
|
148652
149105
|
run: runExecutor
|
|
148653
149106
|
});
|
|
148654
149107
|
|
|
@@ -148715,7 +149168,7 @@ A workflow is a DAG of steps. Schema:
|
|
|
148715
149168
|
- args: templated args object for tool/workflow steps
|
|
148716
149169
|
- needs: [ <upstream step ids> ] (defines the DAG; omit only for true sources)
|
|
148717
149170
|
- when (optional, legacy): simple guards only \u2014 '{{ steps.x.output }} is not empty'. Do NOT use when for semantic decisions (use condition/switch).
|
|
148718
|
-
- onError (optional): fail | continue | retry ; retries (optional, 0-3)
|
|
149171
|
+
- onError (optional): fail | continue | retry ; retries (optional, 0-3 \u2014 only applies when onError is retry; fail/continue always run exactly one attempt)
|
|
148719
149172
|
|
|
148720
149173
|
Operator data \u2014 two ways: declare a value the operator can supply UP FRONT as an \`inputs\` field (filled in before Run). To PAUSE mid-run and ask a question whose answer depends on earlier steps, set \`awaitInput: true\` on a prompt or skill step: the workflow pauses, surfaces the step's prompt to the operator, and resumes with their reply once they answer. Prefer \`inputs\` for known-up-front values; use \`awaitInput\` only for genuinely mid-run questions.
|
|
148721
149174
|
|
|
@@ -149501,46 +149954,61 @@ function buildWorkflowRunner(args) {
|
|
|
149501
149954
|
}
|
|
149502
149955
|
async function resumeNow(runId, reply2) {
|
|
149503
149956
|
const checkpoint = await defaultWorkflowRunStore.load(runId);
|
|
149504
|
-
const
|
|
149505
|
-
|
|
149506
|
-
|
|
149507
|
-
|
|
149508
|
-
|
|
149509
|
-
|
|
149510
|
-
|
|
149511
|
-
|
|
149512
|
-
|
|
149513
|
-
|
|
149514
|
-
|
|
149515
|
-
|
|
149516
|
-
|
|
149517
|
-
|
|
149518
|
-
|
|
149519
|
-
|
|
149520
|
-
|
|
149521
|
-
|
|
149522
|
-
now: () => Date.now(),
|
|
149523
|
-
emit: (subtype, payload) => void session.log.append({
|
|
149524
|
-
type: "plugin_event",
|
|
149525
|
-
sessionId: session.id,
|
|
149526
|
-
turnId,
|
|
149527
|
-
source: "plugin",
|
|
149528
|
-
pluginId: PLUGIN_ID3,
|
|
149529
|
-
subtype,
|
|
149530
|
-
payload
|
|
149531
|
-
}),
|
|
149532
|
-
...logger ? { logger } : {}
|
|
149533
|
-
},
|
|
149534
|
-
defaultWorkflowRunStore
|
|
149535
|
-
);
|
|
149536
|
-
if (result.status === "paused") {
|
|
149537
|
-
logger?.warn?.("workflows: run paused again awaiting operator input; not delivering to inbox", {
|
|
149538
|
-
runId: result.runId
|
|
149957
|
+
const name = checkpoint?.workflow?.name;
|
|
149958
|
+
if (name && inFlight.has(name)) {
|
|
149959
|
+
return {
|
|
149960
|
+
ok: false,
|
|
149961
|
+
status: "failed",
|
|
149962
|
+
steps: [],
|
|
149963
|
+
output: "",
|
|
149964
|
+
error: `workflow "${name}" is already running`
|
|
149965
|
+
};
|
|
149966
|
+
}
|
|
149967
|
+
if (name) inFlight.add(name);
|
|
149968
|
+
try {
|
|
149969
|
+
const turnId = session.startTurn().turnId;
|
|
149970
|
+
const spawner = createSubagentSpawner({
|
|
149971
|
+
parentSession: session,
|
|
149972
|
+
parentTurnId: turnId,
|
|
149973
|
+
parentSignal: session.signal,
|
|
149974
|
+
parentModel: activeModel(session)
|
|
149539
149975
|
});
|
|
149976
|
+
const result = await resumeWorkflowRun(
|
|
149977
|
+
runId,
|
|
149978
|
+
reply2,
|
|
149979
|
+
{
|
|
149980
|
+
spawner,
|
|
149981
|
+
tools: session.tools,
|
|
149982
|
+
lookup: {
|
|
149983
|
+
skill: (n2) => session.skills.byName(n2),
|
|
149984
|
+
workflow: (n2) => store.lookup(n2)
|
|
149985
|
+
},
|
|
149986
|
+
signal: session.signal,
|
|
149987
|
+
now: () => Date.now(),
|
|
149988
|
+
emit: (subtype, payload) => void session.log.append({
|
|
149989
|
+
type: "plugin_event",
|
|
149990
|
+
sessionId: session.id,
|
|
149991
|
+
turnId,
|
|
149992
|
+
source: "plugin",
|
|
149993
|
+
pluginId: PLUGIN_ID3,
|
|
149994
|
+
subtype,
|
|
149995
|
+
payload
|
|
149996
|
+
}),
|
|
149997
|
+
...logger ? { logger } : {}
|
|
149998
|
+
},
|
|
149999
|
+
defaultWorkflowRunStore
|
|
150000
|
+
);
|
|
150001
|
+
if (result.status === "paused") {
|
|
150002
|
+
logger?.warn?.("workflows: run paused again awaiting operator input; not delivering to inbox", {
|
|
150003
|
+
runId: result.runId
|
|
150004
|
+
});
|
|
150005
|
+
return result;
|
|
150006
|
+
}
|
|
150007
|
+
if (checkpoint?.workflow) await deliverToInbox(checkpoint.workflow, result, logger);
|
|
149540
150008
|
return result;
|
|
150009
|
+
} finally {
|
|
150010
|
+
if (name) inFlight.delete(name);
|
|
149541
150011
|
}
|
|
149542
|
-
if (checkpoint?.workflow) await deliverToInbox(checkpoint.workflow, result, logger);
|
|
149543
|
-
return result;
|
|
149544
150012
|
}
|
|
149545
150013
|
return { runNow, resumeNow };
|
|
149546
150014
|
}
|
|
@@ -150018,7 +150486,7 @@ function buildSchedulerRunner(session) {
|
|
|
150018
150486
|
for await (const event of runTurn(session, prompt, model ? { model } : {})) {
|
|
150019
150487
|
if (event.type === "assistant_message") {
|
|
150020
150488
|
text = event.content;
|
|
150021
|
-
|
|
150489
|
+
lastError = event.stopReason === "error" ? "turn ended with error stop reason" : null;
|
|
150022
150490
|
} else if (event.type === "error") {
|
|
150023
150491
|
lastError = event.message;
|
|
150024
150492
|
}
|
|
@@ -150043,7 +150511,7 @@ function buildWebhookRunner(session) {
|
|
|
150043
150511
|
for await (const event of runTurn(target, prompt, model ? { model } : {})) {
|
|
150044
150512
|
if (event.type === "assistant_message") {
|
|
150045
150513
|
text = event.content;
|
|
150046
|
-
|
|
150514
|
+
lastError = event.stopReason === "error" ? "turn ended with error stop reason" : null;
|
|
150047
150515
|
} else if (event.type === "error") {
|
|
150048
150516
|
lastError = event.message;
|
|
150049
150517
|
}
|
|
@@ -150910,7 +151378,7 @@ async function fetchLatest(pkg = DEFAULT_PKG, opts = {}) {
|
|
|
150910
151378
|
}
|
|
150911
151379
|
|
|
150912
151380
|
// src/update/check.ts
|
|
150913
|
-
var
|
|
151381
|
+
var CACHE_TTL_MS2 = 12 * 60 * 60 * 1e3;
|
|
150914
151382
|
var PKG = "@moxxy/cli";
|
|
150915
151383
|
function defaultCacheFile() {
|
|
150916
151384
|
return moxxyPath("update-check.json");
|
|
@@ -150965,7 +151433,7 @@ async function checkForCliUpdate(current, opts = {}) {
|
|
|
150965
151433
|
const file = opts.cacheFile ?? defaultCacheFile();
|
|
150966
151434
|
if (!opts.force) {
|
|
150967
151435
|
const cache4 = readCache(file);
|
|
150968
|
-
if (cache4 && now - cache4.checkedAt <
|
|
151436
|
+
if (cache4 && now - cache4.checkedAt < CACHE_TTL_MS2) {
|
|
150969
151437
|
return shape(current, cache4.latest);
|
|
150970
151438
|
}
|
|
150971
151439
|
}
|
|
@@ -151179,12 +151647,14 @@ async function collectKey(providerId, controller) {
|
|
|
151179
151647
|
if (!controller.testKey) return value;
|
|
151180
151648
|
const s2 = ft2();
|
|
151181
151649
|
s2.start(`Validating ${providerId} key`);
|
|
151650
|
+
let rejected = false;
|
|
151182
151651
|
try {
|
|
151183
151652
|
const result = await controller.testKey(providerId, value);
|
|
151184
151653
|
if (result.ok) {
|
|
151185
151654
|
s2.stop(`${colors.bold("\u2713")} ${providerId} key looks good`);
|
|
151186
151655
|
return value;
|
|
151187
151656
|
}
|
|
151657
|
+
rejected = true;
|
|
151188
151658
|
s2.stop(`${colors.red("\u2717")} ${providerId} rejected the key: ${result.message}`);
|
|
151189
151659
|
} catch (err) {
|
|
151190
151660
|
s2.stop(
|
|
@@ -151197,6 +151667,7 @@ async function collectKey(providerId, controller) {
|
|
|
151197
151667
|
});
|
|
151198
151668
|
const retry = guard(retryRaw);
|
|
151199
151669
|
if (!retry) {
|
|
151670
|
+
if (rejected) bail();
|
|
151200
151671
|
return value;
|
|
151201
151672
|
}
|
|
151202
151673
|
}
|
|
@@ -152435,7 +152906,7 @@ ${available}`
|
|
|
152435
152906
|
async function runChannelsCommand(argv) {
|
|
152436
152907
|
const [name, sub, ...rest] = argv.positional;
|
|
152437
152908
|
if (!name || name === "list") {
|
|
152438
|
-
return runList2();
|
|
152909
|
+
return runList2(argv);
|
|
152439
152910
|
}
|
|
152440
152911
|
const outcome = await probeSession(
|
|
152441
152912
|
argvToSetupOptions(argv, {
|
|
@@ -152489,12 +152960,13 @@ ${available}`
|
|
|
152489
152960
|
if (outcome !== "run-channel") return outcome.code;
|
|
152490
152961
|
return runChannelByName(name, argv);
|
|
152491
152962
|
}
|
|
152492
|
-
async function runList2() {
|
|
152963
|
+
async function runList2(argv) {
|
|
152493
152964
|
const { entries, config } = await probeSession(
|
|
152494
|
-
argvToSetupOptions(
|
|
152495
|
-
|
|
152496
|
-
|
|
152497
|
-
|
|
152965
|
+
argvToSetupOptions(argv, {
|
|
152966
|
+
skipKeyPrompt: true,
|
|
152967
|
+
tolerateNoProvider: true,
|
|
152968
|
+
skipProviderActivation: true
|
|
152969
|
+
}),
|
|
152498
152970
|
async ({ session, vault, config: config2 }) => ({
|
|
152499
152971
|
config: config2,
|
|
152500
152972
|
entries: await session.channels.listWithAvailability({
|
|
@@ -153171,7 +153643,7 @@ function unitPath(spec) {
|
|
|
153171
153643
|
}
|
|
153172
153644
|
function renderUnit(spec, ctx) {
|
|
153173
153645
|
const execStart = [ctx.node, ctx.cli, ...spec.execArgs].map(quote).join(" ");
|
|
153174
|
-
const envLines = Object.entries(spec.env ?? {}).map(([k3, v3]) => `Environment=${k3}=${v3}`).join("\n");
|
|
153646
|
+
const envLines = Object.entries(spec.env ?? {}).map(([k3, v3]) => `Environment=${k3}=${envValue(v3)}`).join("\n");
|
|
153175
153647
|
return `[Unit]
|
|
153176
153648
|
Description=${spec.description}
|
|
153177
153649
|
After=network-online.target
|
|
@@ -153195,6 +153667,11 @@ function quote(s2) {
|
|
|
153195
153667
|
if (!/[\s"]/.test(s2)) return s2;
|
|
153196
153668
|
return '"' + s2.replace(/"/g, '\\"') + '"';
|
|
153197
153669
|
}
|
|
153670
|
+
function envValue(v3) {
|
|
153671
|
+
const s2 = v3.replace(/[\r\n]+/g, " ");
|
|
153672
|
+
if (!/[\s"\\]/.test(s2)) return s2;
|
|
153673
|
+
return '"' + s2.replace(/\\/g, "\\\\").replace(/"/g, '\\"') + '"';
|
|
153674
|
+
}
|
|
153198
153675
|
var systemdService = {
|
|
153199
153676
|
platform: "linux",
|
|
153200
153677
|
async getStatus(spec) {
|
|
@@ -153423,21 +153900,23 @@ async function runDaemonBackground() {
|
|
|
153423
153900
|
return result.ok ? 0 : 1;
|
|
153424
153901
|
}
|
|
153425
153902
|
async function runDaemonForeground() {
|
|
153426
|
-
const { session
|
|
153903
|
+
const { session } = await setupSessionWithConfig({ cwd: process.cwd() });
|
|
153427
153904
|
process.stdout.write(
|
|
153428
153905
|
`${colors.bold("scheduler daemon")} ${colors.dim("provider=" + (session.providers.getActiveName() ?? "(none)"))}
|
|
153429
153906
|
` + colors.dim(" ^C to stop. Schedules fire while this process is alive.\n")
|
|
153430
153907
|
);
|
|
153431
153908
|
let stopRequested = false;
|
|
153432
|
-
const shutdown = async () => {
|
|
153909
|
+
const shutdown = async (signal) => {
|
|
153433
153910
|
if (stopRequested) return;
|
|
153434
153911
|
stopRequested = true;
|
|
153912
|
+
const force = setTimeout(() => process.exit(0), 4e3);
|
|
153913
|
+
force.unref?.();
|
|
153435
153914
|
process.stdout.write("\nstopping scheduler\u2026\n");
|
|
153436
|
-
await
|
|
153915
|
+
await session.close(signal).catch(() => void 0);
|
|
153437
153916
|
process.exit(0);
|
|
153438
153917
|
};
|
|
153439
|
-
process.on("SIGINT", () => void shutdown());
|
|
153440
|
-
process.on("SIGTERM", () => void shutdown());
|
|
153918
|
+
process.on("SIGINT", () => void shutdown("SIGINT"));
|
|
153919
|
+
process.on("SIGTERM", () => void shutdown("SIGTERM"));
|
|
153441
153920
|
setInterval(() => {
|
|
153442
153921
|
}, 6e4);
|
|
153443
153922
|
return await new Promise(() => {
|