@moxxy/cli 0.8.2 → 0.10.0
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 +305 -17
- package/dist/bin.js.map +1 -1
- package/package.json +2 -2
package/dist/bin.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import { z as z$1, defineProvider, definePlugin, defineTool, MoxxyError, writeFileAtomic, asTurnId, defineMode, asPluginId, defineChannel, defineTunnelProvider, spawnCliTunnel, isCliTunnelAvailable, createMutex, defineWorkflowExecutor, toFriendlyError, estimateTextTokens, classifyHttpStatus, createStuckLoopDetector, runCompactionIfNeeded, runElisionIfNeeded, collectProviderStream, usageEventFields, isContextOverflowError, emitRequestsAndDetectStuck, executeToolUses, buildSystemPromptWithSkills, projectMessages, defineCompactor, defineCacheStrategy, denyByDefaultResolver, createAllowListResolver, moxxyPath, zodToJsonSchema, runSingleShotTurn, bearerTokenMatches, resolveChannelToken, rotateChannelToken, estimateContextTokens as estimateContextTokens$1, readRequestBody, MOXXY_WS_SUBPROTOCOL, defineEmbedder, bearerGuard, tokenFromWsProtocolHeader, skillFrontmatterSchema, asSkillId, getInstallHint, moxxyHome, defineTranscriber, summarizeTokensByModel,
|
|
3
|
+
import { z as z$1, defineProvider, definePlugin, defineTool, MoxxyError, writeFileAtomic, asTurnId, defineMode, asPluginId, defineChannel, defineTunnelProvider, spawnCliTunnel, isCliTunnelAvailable, createMutex, defineWorkflowExecutor, toFriendlyError, estimateTextTokens, classifyHttpStatus, createStuckLoopDetector, runCompactionIfNeeded, runElisionIfNeeded, collectProviderStream, usageEventFields, isContextOverflowError, emitRequestsAndDetectStuck, executeToolUses, buildSystemPromptWithSkills, projectMessages, defineCompactor, defineCacheStrategy, denyByDefaultResolver, createAllowListResolver, moxxyPath, zodToJsonSchema, runSingleShotTurn, bearerTokenMatches, resolveChannelToken, rotateChannelToken, estimateContextTokens as estimateContextTokens$1, readRequestBody, MOXXY_WS_SUBPROTOCOL, defineEmbedder, migrateModeName, bearerGuard, tokenFromWsProtocolHeader, skillFrontmatterSchema, asSkillId, getInstallHint, moxxyHome, defineTranscriber, summarizeTokensByModel, createDeferredPermissionResolver, classifyNetworkError, addModelTotals, ISOLATION_RANK, moxxyPackageSchema, defineCommand, createCallbackResolver, autoAllowResolver, asSessionId, asToolCallId, defineViewRenderer, DEFAULT_VIEW_TAGS, isSafeViewUrl, evaluateToolRule, summarizeSessionTokensFromEvents, computeElisionState, toolResultStubbed, toolResultStub, toolResultBytes, conversationalStubbed, conversationalStub, asEventId } from '@moxxy/sdk';
|
|
4
4
|
import * as fs27 from 'fs';
|
|
5
5
|
import fs27__default, { existsSync, promises, ReadStream, readFileSync, statSync, readdirSync, mkdirSync, writeFileSync, unlinkSync, renameSync, watch, createReadStream } from 'fs';
|
|
6
6
|
import * as path3 from 'path';
|
|
@@ -1376,6 +1376,13 @@ var init_providers = __esm({
|
|
|
1376
1376
|
defs = /* @__PURE__ */ new Map();
|
|
1377
1377
|
instances = /* @__PURE__ */ new Map();
|
|
1378
1378
|
active = null;
|
|
1379
|
+
/**
|
|
1380
|
+
* Names the user disabled. Kept name-based (not def-based) so it can be
|
|
1381
|
+
* seeded from preferences BEFORE the plugins register their defs — boot
|
|
1382
|
+
* order doesn't matter. A disabled provider stays registered/listable but
|
|
1383
|
+
* can't be activated.
|
|
1384
|
+
*/
|
|
1385
|
+
disabled = /* @__PURE__ */ new Set();
|
|
1379
1386
|
/**
|
|
1380
1387
|
* Register a provider def. Throws on duplicate — use `replace()` for
|
|
1381
1388
|
* explicit overwrite. Matches the semantics of `tools` and `channels`.
|
|
@@ -1404,10 +1411,32 @@ var init_providers = __esm({
|
|
|
1404
1411
|
list() {
|
|
1405
1412
|
return [...this.defs.values()];
|
|
1406
1413
|
}
|
|
1414
|
+
/**
|
|
1415
|
+
* Enable/disable a provider by name. Disabling the ACTIVE provider is
|
|
1416
|
+
* refused — switch first, then disable — so a session never ends up with an
|
|
1417
|
+
* active-but-disabled provider. Unknown names are accepted (the set seeds
|
|
1418
|
+
* from preferences before plugins register), except when disabling via a
|
|
1419
|
+
* live toggle is meaningless because the provider is active.
|
|
1420
|
+
*/
|
|
1421
|
+
setEnabled(name, enabled) {
|
|
1422
|
+
if (!enabled && this.active === name) {
|
|
1423
|
+
throw new Error(`Cannot disable the active provider "${name}" \u2014 switch providers first.`);
|
|
1424
|
+
}
|
|
1425
|
+
if (enabled)
|
|
1426
|
+
this.disabled.delete(name);
|
|
1427
|
+
else
|
|
1428
|
+
this.disabled.add(name);
|
|
1429
|
+
}
|
|
1430
|
+
isEnabled(name) {
|
|
1431
|
+
return !this.disabled.has(name);
|
|
1432
|
+
}
|
|
1407
1433
|
setActive(name, config) {
|
|
1408
1434
|
const def = this.defs.get(name);
|
|
1409
1435
|
if (!def)
|
|
1410
1436
|
throw new Error(`Provider not registered: ${name}`);
|
|
1437
|
+
if (this.disabled.has(name)) {
|
|
1438
|
+
throw new Error(`Provider "${name}" is disabled \u2014 enable it first.`);
|
|
1439
|
+
}
|
|
1411
1440
|
let instance = this.instances.get(name);
|
|
1412
1441
|
if (!instance) {
|
|
1413
1442
|
instance = def.createClient(config ?? {});
|
|
@@ -3081,6 +3110,7 @@ var init_session = __esm({
|
|
|
3081
3110
|
readyProviders;
|
|
3082
3111
|
credentialResolver;
|
|
3083
3112
|
mcpAdmin;
|
|
3113
|
+
providerAdmin;
|
|
3084
3114
|
workflows;
|
|
3085
3115
|
pluginsAdmin;
|
|
3086
3116
|
dispatcher;
|
|
@@ -3262,7 +3292,8 @@ var init_session = __esm({
|
|
|
3262
3292
|
// are the ones the desktop's "Fetch live" affordance targets;
|
|
3263
3293
|
// they advertise this via the provider-admin factory by
|
|
3264
3294
|
// setting `supportsLiveModelDiscovery: true` on their def.
|
|
3265
|
-
supportsLiveModelDiscovery: p3.supportsLiveModelDiscovery === true
|
|
3295
|
+
supportsLiveModelDiscovery: p3.supportsLiveModelDiscovery === true,
|
|
3296
|
+
enabled: this.providers.isEnabled(p3.name)
|
|
3266
3297
|
})),
|
|
3267
3298
|
activeMode,
|
|
3268
3299
|
activeModeBadge,
|
|
@@ -127441,6 +127472,15 @@ function noteTemperatureUnsupported() {
|
|
|
127441
127472
|
console.debug("[openai-codex] ProviderRequest.temperature is not supported by the Codex Responses backend (gpt-5 reasoning models reject sampling params); ignoring it.");
|
|
127442
127473
|
}
|
|
127443
127474
|
}
|
|
127475
|
+
var maxTokensNoteShown = false;
|
|
127476
|
+
function noteMaxTokensUnsupported() {
|
|
127477
|
+
if (maxTokensNoteShown)
|
|
127478
|
+
return;
|
|
127479
|
+
maxTokensNoteShown = true;
|
|
127480
|
+
if (process.env.MOXXY_DEBUG) {
|
|
127481
|
+
console.debug("[openai-codex] ProviderRequest.maxTokens is not supported by the Codex Responses backend (400 Unsupported parameter: max_output_tokens); ignoring it.");
|
|
127482
|
+
}
|
|
127483
|
+
}
|
|
127444
127484
|
function toResponsesBody(req, opts = {}) {
|
|
127445
127485
|
const instructions = extractSystemText(req.messages, req.system) || DEFAULT_INSTRUCTIONS;
|
|
127446
127486
|
const body = {
|
|
@@ -127458,7 +127498,7 @@ function toResponsesBody(req, opts = {}) {
|
|
|
127458
127498
|
if (opts.sessionHint)
|
|
127459
127499
|
body.prompt_cache_key = opts.sessionHint;
|
|
127460
127500
|
if (req.maxTokens !== void 0)
|
|
127461
|
-
|
|
127501
|
+
noteMaxTokensUnsupported();
|
|
127462
127502
|
if (req.temperature !== void 0)
|
|
127463
127503
|
noteTemperatureUnsupported();
|
|
127464
127504
|
return body;
|
|
@@ -134374,13 +134414,14 @@ async function createWebSocketTransportServer(opts) {
|
|
|
134374
134414
|
const host = opts.host ?? "127.0.0.1";
|
|
134375
134415
|
const maxConnections = opts.maxConnections ?? DEFAULT_MAX_CONNECTIONS;
|
|
134376
134416
|
let currentToken = opts.authToken;
|
|
134417
|
+
let currentAllowedOrigins = opts.allowedOrigins ?? [];
|
|
134377
134418
|
let connections = 0;
|
|
134378
134419
|
const wss = new import_websocket_server.default({
|
|
134379
134420
|
host,
|
|
134380
134421
|
port: opts.port,
|
|
134381
134422
|
maxPayload: opts.maxPayloadBytes ?? 64 * 1024 * 1024,
|
|
134382
134423
|
verifyClient: (info) => {
|
|
134383
|
-
if (!checkWsOrigin(info.req,
|
|
134424
|
+
if (!checkWsOrigin(info.req, currentAllowedOrigins)) {
|
|
134384
134425
|
console.warn(`[moxxy] ws bridge: rejected browser-origin upgrade (Origin: ${String(info.req.headers.origin)})`);
|
|
134385
134426
|
return false;
|
|
134386
134427
|
}
|
|
@@ -134420,6 +134461,9 @@ async function createWebSocketTransportServer(opts) {
|
|
|
134420
134461
|
for (const client of wss.clients)
|
|
134421
134462
|
client.terminate();
|
|
134422
134463
|
},
|
|
134464
|
+
setAllowedOrigins(origins) {
|
|
134465
|
+
currentAllowedOrigins = [...origins];
|
|
134466
|
+
},
|
|
134423
134467
|
clientCount() {
|
|
134424
134468
|
return connections;
|
|
134425
134469
|
},
|
|
@@ -134753,7 +134797,7 @@ function isRunnerUp(socketPath = runnerSocketPath()) {
|
|
|
134753
134797
|
|
|
134754
134798
|
// ../runner/dist/server.js
|
|
134755
134799
|
init_dist();
|
|
134756
|
-
var RUNNER_PROTOCOL_VERSION =
|
|
134800
|
+
var RUNNER_PROTOCOL_VERSION = 7;
|
|
134757
134801
|
var MIN_COMPATIBLE_PROTOCOL_VERSION = 1;
|
|
134758
134802
|
var RunnerMethod = {
|
|
134759
134803
|
/** client->server: handshake; returns the initial info snapshot. */
|
|
@@ -134778,6 +134822,12 @@ var RunnerMethod = {
|
|
|
134778
134822
|
ModeSetActive: "mode.setActive",
|
|
134779
134823
|
/** client->server: switch the active provider (server resolves credentials). */
|
|
134780
134824
|
ProviderSetActive: "provider.setActive",
|
|
134825
|
+
/** client->server: enable/disable a provider (v7; persists to preferences). */
|
|
134826
|
+
ProviderSetEnabled: "provider.setEnabled",
|
|
134827
|
+
/** client->server: re-probe every provider's credentials → readyProviders (v7). */
|
|
134828
|
+
ProviderRefreshReady: "provider.refreshReady",
|
|
134829
|
+
/** client->server: patch a stored (runtime-registered) provider's config (v7). */
|
|
134830
|
+
ProviderConfigure: "provider.configure",
|
|
134781
134831
|
/** client->server: persist an allow-always permission rule. */
|
|
134782
134832
|
PermissionAddAllow: "permission.addAllow",
|
|
134783
134833
|
/** client->server: run a registered slash command on the runner. */
|
|
@@ -134873,6 +134923,19 @@ var providerSetActiveParamsSchema = z.object({
|
|
|
134873
134923
|
name: z.string(),
|
|
134874
134924
|
config: z.record(z.unknown()).optional()
|
|
134875
134925
|
});
|
|
134926
|
+
var providerSetEnabledParamsSchema = z.object({
|
|
134927
|
+
name: z.string().min(1),
|
|
134928
|
+
enabled: z.boolean()
|
|
134929
|
+
});
|
|
134930
|
+
var providerConfigureParamsSchema = z.object({
|
|
134931
|
+
name: z.string().min(1),
|
|
134932
|
+
patch: z.object({
|
|
134933
|
+
baseURL: z.string().url().optional(),
|
|
134934
|
+
defaultModel: z.string().min(1).optional(),
|
|
134935
|
+
envVar: z.string().regex(/^[A-Z][A-Z0-9_]*$/).optional(),
|
|
134936
|
+
models: z.array(z.object({ id: z.string().min(1), contextWindow: z.number() }).passthrough()).min(1).optional()
|
|
134937
|
+
})
|
|
134938
|
+
});
|
|
134876
134939
|
var permissionAddAllowParamsSchema = z.object({
|
|
134877
134940
|
name: z.string(),
|
|
134878
134941
|
reason: z.string().optional()
|
|
@@ -134979,6 +135042,9 @@ var RunnerServer = class {
|
|
|
134979
135042
|
peer.handle(RunnerMethod.SetResolver, (raw) => this.handleSetResolver(client, raw));
|
|
134980
135043
|
peer.handle(RunnerMethod.ModeSetActive, (raw) => this.handleModeSetActive(raw));
|
|
134981
135044
|
peer.handle(RunnerMethod.ProviderSetActive, (raw) => this.handleProviderSetActive(raw));
|
|
135045
|
+
peer.handle(RunnerMethod.ProviderSetEnabled, (raw) => this.handleProviderSetEnabled(raw));
|
|
135046
|
+
peer.handle(RunnerMethod.ProviderRefreshReady, () => this.handleProviderRefreshReady());
|
|
135047
|
+
peer.handle(RunnerMethod.ProviderConfigure, (raw) => this.handleProviderConfigure(raw));
|
|
134982
135048
|
peer.handle(RunnerMethod.PermissionAddAllow, (raw) => this.handlePermissionAddAllow(raw));
|
|
134983
135049
|
peer.handle(RunnerMethod.CommandRun, (raw) => this.handleCommandRun(raw));
|
|
134984
135050
|
peer.handle(RunnerMethod.Transcribe, (raw) => this.handleTranscribe(raw));
|
|
@@ -135054,6 +135120,7 @@ var RunnerServer = class {
|
|
|
135054
135120
|
turnId,
|
|
135055
135121
|
...error2 ? { error: error2 } : {}
|
|
135056
135122
|
});
|
|
135123
|
+
this.broadcastInfo();
|
|
135057
135124
|
}
|
|
135058
135125
|
});
|
|
135059
135126
|
return { turnId };
|
|
@@ -135118,6 +135185,54 @@ var RunnerServer = class {
|
|
|
135118
135185
|
this.broadcastInfo();
|
|
135119
135186
|
return {};
|
|
135120
135187
|
}
|
|
135188
|
+
async handleProviderSetEnabled(raw) {
|
|
135189
|
+
const { name, enabled } = providerSetEnabledParamsSchema.parse(raw);
|
|
135190
|
+
if (!this.session.providers.list().some((p3) => p3.name === name)) {
|
|
135191
|
+
throw new Error(`Provider not registered: ${name}`);
|
|
135192
|
+
}
|
|
135193
|
+
this.session.providers.setEnabled(name, enabled);
|
|
135194
|
+
void (async () => {
|
|
135195
|
+
const prefs = await loadPreferences();
|
|
135196
|
+
const current = new Set(prefs.disabledProviders ?? []);
|
|
135197
|
+
if (enabled)
|
|
135198
|
+
current.delete(name);
|
|
135199
|
+
else
|
|
135200
|
+
current.add(name);
|
|
135201
|
+
await savePreferences({ disabledProviders: [...current] });
|
|
135202
|
+
})();
|
|
135203
|
+
this.broadcastInfo();
|
|
135204
|
+
return {};
|
|
135205
|
+
}
|
|
135206
|
+
async handleProviderRefreshReady() {
|
|
135207
|
+
const resolver2 = this.session.credentialResolver;
|
|
135208
|
+
if (resolver2) {
|
|
135209
|
+
const ready = /* @__PURE__ */ new Set();
|
|
135210
|
+
const active = this.session.providers.getActiveName();
|
|
135211
|
+
if (active)
|
|
135212
|
+
ready.add(active);
|
|
135213
|
+
for (const p3 of this.session.providers.list()) {
|
|
135214
|
+
if (ready.has(p3.name))
|
|
135215
|
+
continue;
|
|
135216
|
+
try {
|
|
135217
|
+
await resolver2(p3.name);
|
|
135218
|
+
ready.add(p3.name);
|
|
135219
|
+
} catch {
|
|
135220
|
+
}
|
|
135221
|
+
}
|
|
135222
|
+
this.session.readyProviders = ready;
|
|
135223
|
+
}
|
|
135224
|
+
this.broadcastInfo();
|
|
135225
|
+
return {};
|
|
135226
|
+
}
|
|
135227
|
+
async handleProviderConfigure(raw) {
|
|
135228
|
+
const { name, patch } = providerConfigureParamsSchema.parse(raw);
|
|
135229
|
+
const admin = this.session.providerAdmin;
|
|
135230
|
+
if (!admin)
|
|
135231
|
+
throw new Error("provider admin not supported on this runner");
|
|
135232
|
+
await admin.configure(name, patch);
|
|
135233
|
+
this.broadcastInfo();
|
|
135234
|
+
return {};
|
|
135235
|
+
}
|
|
135121
135236
|
async handlePermissionAddAllow(raw) {
|
|
135122
135237
|
const { name, reason } = permissionAddAllowParamsSchema.parse(raw);
|
|
135123
135238
|
await this.session.permissions.addAllow({ name, ...reason ? { reason } : {} });
|
|
@@ -135393,6 +135508,7 @@ var RemoteSession = class {
|
|
|
135393
135508
|
requirements;
|
|
135394
135509
|
permissions;
|
|
135395
135510
|
mcpAdmin;
|
|
135511
|
+
providerAdmin;
|
|
135396
135512
|
workflows;
|
|
135397
135513
|
/**
|
|
135398
135514
|
* Turns that completed before their `runTurn` stream was registered. A fast
|
|
@@ -135406,6 +135522,8 @@ var RemoteSession = class {
|
|
|
135406
135522
|
permissionResolver = null;
|
|
135407
135523
|
approvalResolver = null;
|
|
135408
135524
|
info = null;
|
|
135525
|
+
/** Subscribers to `info.changed` pushes (see {@link onInfoChanged}). */
|
|
135526
|
+
infoListeners = /* @__PURE__ */ new Set();
|
|
135409
135527
|
/**
|
|
135410
135528
|
* The protocol version the SERVER reported at attach. Defaults to our own
|
|
135411
135529
|
* version until the handshake resolves. Version-specific client methods (the
|
|
@@ -135431,6 +135549,12 @@ var RemoteSession = class {
|
|
|
135431
135549
|
});
|
|
135432
135550
|
this.peer.on(RunnerNotification.InfoChanged, (params) => {
|
|
135433
135551
|
this.info = params.info;
|
|
135552
|
+
for (const fn of this.infoListeners) {
|
|
135553
|
+
try {
|
|
135554
|
+
fn(this.info);
|
|
135555
|
+
} catch {
|
|
135556
|
+
}
|
|
135557
|
+
}
|
|
135434
135558
|
});
|
|
135435
135559
|
this.peer.on(RunnerNotification.ReplayStart, (params) => {
|
|
135436
135560
|
const { fromSeq } = params;
|
|
@@ -135468,6 +135592,7 @@ var RemoteSession = class {
|
|
|
135468
135592
|
this.requirements = { check: () => ({ ready: false, issues: [] }) };
|
|
135469
135593
|
this.permissions = this.makePermissionsView();
|
|
135470
135594
|
this.mcpAdmin = this.makeMcpAdminView();
|
|
135595
|
+
this.providerAdmin = this.makeProviderAdminView();
|
|
135471
135596
|
this.workflows = this.makeWorkflowsView();
|
|
135472
135597
|
}
|
|
135473
135598
|
/**
|
|
@@ -135533,6 +135658,16 @@ var RemoteSession = class {
|
|
|
135533
135658
|
getInfo() {
|
|
135534
135659
|
return this.requireInfo();
|
|
135535
135660
|
}
|
|
135661
|
+
/**
|
|
135662
|
+
* Subscribe to runner `info.changed` pushes (registry snapshot changes —
|
|
135663
|
+
* provider/mode/MCP/workflow mutations, including ones made by tools inside
|
|
135664
|
+
* a turn). Fires after the local `getInfo()` mirror has been updated, so a
|
|
135665
|
+
* listener can re-read it synchronously. Returns an unsubscribe fn.
|
|
135666
|
+
*/
|
|
135667
|
+
onInfoChanged(fn) {
|
|
135668
|
+
this.infoListeners.add(fn);
|
|
135669
|
+
return () => this.infoListeners.delete(fn);
|
|
135670
|
+
}
|
|
135536
135671
|
async *runTurn(prompt, opts = {}) {
|
|
135537
135672
|
const result = await this.peer.request(RunnerMethod.RunTurn, {
|
|
135538
135673
|
prompt,
|
|
@@ -135736,6 +135871,27 @@ var RemoteSession = class {
|
|
|
135736
135871
|
detach: (name) => this.peer.request(RunnerMethod.McpDetach, { name })
|
|
135737
135872
|
};
|
|
135738
135873
|
}
|
|
135874
|
+
// Provider management (protocol v7): backs the desktop's interactive
|
|
135875
|
+
// Settings → Providers tab. Gated on the SERVER's reported version so a v7
|
|
135876
|
+
// client attached to an older runner (a desktop whose JS hot-update outran
|
|
135877
|
+
// its bundled CLI) gets a clear "update the CLI" error instead of a raw
|
|
135878
|
+
// method-not-found.
|
|
135879
|
+
makeProviderAdminView() {
|
|
135880
|
+
return {
|
|
135881
|
+
setEnabled: async (name, enabled) => {
|
|
135882
|
+
this.requireServerProtocol(7, "Enabling/disabling a provider");
|
|
135883
|
+
await this.peer.request(RunnerMethod.ProviderSetEnabled, { name, enabled });
|
|
135884
|
+
},
|
|
135885
|
+
refreshReady: async () => {
|
|
135886
|
+
this.requireServerProtocol(7, "Re-probing provider credentials");
|
|
135887
|
+
await this.peer.request(RunnerMethod.ProviderRefreshReady, {});
|
|
135888
|
+
},
|
|
135889
|
+
configure: async (name, patch) => {
|
|
135890
|
+
this.requireServerProtocol(7, "Configuring a provider");
|
|
135891
|
+
await this.peer.request(RunnerMethod.ProviderConfigure, { name, patch });
|
|
135892
|
+
}
|
|
135893
|
+
};
|
|
135894
|
+
}
|
|
135739
135895
|
makeWorkflowsView() {
|
|
135740
135896
|
return {
|
|
135741
135897
|
list: () => this.peer.request(RunnerMethod.WorkflowList),
|
|
@@ -135994,6 +136150,17 @@ var REMOTE_ALLOWED_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
135994
136150
|
"session.setMode",
|
|
135995
136151
|
"session.newSession",
|
|
135996
136152
|
"session.runCommand",
|
|
136153
|
+
// Multi-session conversations: list/create/switch/rename are conversation-
|
|
136154
|
+
// scoped — the same trust class as `session.newSession` (already allowed),
|
|
136155
|
+
// and what a paired phone needs to mirror the desktop's session list.
|
|
136156
|
+
// `sessions.remove` is deliberately NOT here: it deletes on-disk state
|
|
136157
|
+
// (the runner's session JSONL + the chat NDJSON transcript), a destructive
|
|
136158
|
+
// host mutation in the same class as `desks.remove`, which is also
|
|
136159
|
+
// host-only.
|
|
136160
|
+
"sessions.list",
|
|
136161
|
+
"sessions.create",
|
|
136162
|
+
"sessions.setActive",
|
|
136163
|
+
"sessions.rename",
|
|
135997
136164
|
// Voice input (capability-probed; transcribe fails coded without a transcriber).
|
|
135998
136165
|
"session.hasTranscriber",
|
|
135999
136166
|
"session.transcribe",
|
|
@@ -136129,6 +136296,20 @@ var ipcInputSchemas = {
|
|
|
136129
136296
|
id: z.string().min(1).max(256),
|
|
136130
136297
|
name: z.string().min(1).max(200)
|
|
136131
136298
|
}),
|
|
136299
|
+
// Sessions: create/rename persist the name into the desks JSON (bound it
|
|
136300
|
+
// like desks.create/rename); setActive spawns a runner and remove deletes
|
|
136301
|
+
// the session's on-disk logs, so their ids are bounded too. These commands
|
|
136302
|
+
// are also served to remote (WS) clients, so the bounds are load-bearing.
|
|
136303
|
+
"sessions.create": z.object({
|
|
136304
|
+
deskId: z.string().min(1).max(256).optional(),
|
|
136305
|
+
name: z.string().min(1).max(200).optional()
|
|
136306
|
+
}).optional(),
|
|
136307
|
+
"sessions.setActive": z.object({ id: z.string().min(1).max(256) }),
|
|
136308
|
+
"sessions.remove": z.object({ id: z.string().min(1).max(256) }),
|
|
136309
|
+
"sessions.rename": z.object({
|
|
136310
|
+
id: z.string().min(1).max(256),
|
|
136311
|
+
name: z.string().min(1).max(200)
|
|
136312
|
+
}),
|
|
136132
136313
|
// Whitelist the fields a renderer may write — `version` is managed by
|
|
136133
136314
|
// the main process; unknown keys are rejected (.strict()).
|
|
136134
136315
|
"prefs.update": z.object({
|
|
@@ -136136,7 +136317,8 @@ var ipcInputSchemas = {
|
|
|
136136
136317
|
clerkUserId: z.string().max(256).nullable().optional(),
|
|
136137
136318
|
clerkDisplayName: z.string().max(256).nullable().optional(),
|
|
136138
136319
|
signedInAt: z.number().nullable().optional(),
|
|
136139
|
-
mobileGatewayEnabled: z.boolean().optional()
|
|
136320
|
+
mobileGatewayEnabled: z.boolean().optional(),
|
|
136321
|
+
theme: z.enum(["light", "dark", "system"]).optional()
|
|
136140
136322
|
}).strict(),
|
|
136141
136323
|
// Mobile-gateway control. Both no-arg variants pin the payload to "nothing"
|
|
136142
136324
|
// so a hostile caller can't smuggle args; setEnabled is a strict boolean.
|
|
@@ -136524,14 +136706,41 @@ function isWildcardHost(host) {
|
|
|
136524
136706
|
const h3 = host.trim().toLowerCase();
|
|
136525
136707
|
return h3 === "0.0.0.0" || h3 === "::" || h3 === "[::]";
|
|
136526
136708
|
}
|
|
136709
|
+
var VIRTUAL_IFACE = /^(?:utun|tun|tap|ppp|ipsec|wg|zt|ts|tailscale|vmnet|vnic|bridge|docker|veth|awdl|llw|ap|anpi)\d*$/i;
|
|
136710
|
+
function isLinkLocalV4(ip) {
|
|
136711
|
+
return ip.startsWith("169.254.");
|
|
136712
|
+
}
|
|
136713
|
+
function isRfc1918(ip) {
|
|
136714
|
+
if (ip.startsWith("10.") || ip.startsWith("192.168."))
|
|
136715
|
+
return true;
|
|
136716
|
+
const m3 = /^172\.(\d{1,3})\./.exec(ip);
|
|
136717
|
+
return m3 !== null && Number(m3[1]) >= 16 && Number(m3[1]) <= 31;
|
|
136718
|
+
}
|
|
136719
|
+
function isCgnat(ip) {
|
|
136720
|
+
const m3 = /^100\.(\d{1,3})\./.exec(ip);
|
|
136721
|
+
return m3 !== null && Number(m3[1]) >= 64 && Number(m3[1]) <= 127;
|
|
136722
|
+
}
|
|
136527
136723
|
function lanHost(fallback) {
|
|
136528
|
-
|
|
136724
|
+
let best = null;
|
|
136725
|
+
for (const [name, list] of Object.entries(os5__default.networkInterfaces())) {
|
|
136529
136726
|
for (const ni of list ?? []) {
|
|
136530
|
-
if (ni.family
|
|
136531
|
-
|
|
136727
|
+
if (ni.family !== "IPv4" || ni.internal)
|
|
136728
|
+
continue;
|
|
136729
|
+
const virtual = VIRTUAL_IFACE.test(name);
|
|
136730
|
+
let rank;
|
|
136731
|
+
if (isLinkLocalV4(ni.address))
|
|
136732
|
+
rank = 5;
|
|
136733
|
+
else if (isRfc1918(ni.address))
|
|
136734
|
+
rank = virtual ? 3 : 1;
|
|
136735
|
+
else if (isCgnat(ni.address))
|
|
136736
|
+
rank = 4;
|
|
136737
|
+
else
|
|
136738
|
+
rank = virtual ? 4 : 2;
|
|
136739
|
+
if (!best || rank < best.rank)
|
|
136740
|
+
best = { address: ni.address, rank };
|
|
136532
136741
|
}
|
|
136533
136742
|
}
|
|
136534
|
-
return fallback;
|
|
136743
|
+
return best?.address ?? fallback;
|
|
136535
136744
|
}
|
|
136536
136745
|
function advertisedHost(bindHost) {
|
|
136537
136746
|
if (isWildcardHost(bindHost))
|
|
@@ -136547,6 +136756,20 @@ function buildConnectUrl(opts) {
|
|
|
136547
136756
|
}
|
|
136548
136757
|
return `ws://${opts.localHost}:${opts.port}/?t=${t2}`;
|
|
136549
136758
|
}
|
|
136759
|
+
function connectUrlOrigin(url2) {
|
|
136760
|
+
const u2 = new URL(url2);
|
|
136761
|
+
const secure = u2.protocol === "wss:" || u2.protocol === "https:";
|
|
136762
|
+
return `${secure ? "https" : "http"}://${u2.host}`;
|
|
136763
|
+
}
|
|
136764
|
+
function advertisedOrigins(bindHost, port) {
|
|
136765
|
+
return [
|
|
136766
|
+
.../* @__PURE__ */ new Set([
|
|
136767
|
+
connectUrlOrigin(`ws://${advertisedHost(bindHost)}:${port}`),
|
|
136768
|
+
`http://127.0.0.1:${port}`,
|
|
136769
|
+
`http://localhost:${port}`
|
|
136770
|
+
])
|
|
136771
|
+
];
|
|
136772
|
+
}
|
|
136550
136773
|
|
|
136551
136774
|
// ../plugin-channel-mobile/dist/tunnel.js
|
|
136552
136775
|
function normalizeTunnelChoice(raw) {
|
|
@@ -136631,10 +136854,12 @@ var MobileChannel = class {
|
|
|
136631
136854
|
this.host = host;
|
|
136632
136855
|
host.register();
|
|
136633
136856
|
host.wire();
|
|
136857
|
+
const localOrigins = advertisedOrigins(this.bindHost, this.port);
|
|
136634
136858
|
const server = await startWsBridge(bus, {
|
|
136635
136859
|
port: this.port,
|
|
136636
136860
|
host: this.bindHost,
|
|
136637
136861
|
authToken: this.token,
|
|
136862
|
+
allowedOrigins: localOrigins,
|
|
136638
136863
|
// Back-compat ONLY: the QR this channel prints embeds the token as `?t=`
|
|
136639
136864
|
// (pairing payload); current apps strip it and authenticate via the
|
|
136640
136865
|
// Sec-WebSocket-Protocol bearer entry, but older installed builds still
|
|
@@ -136649,6 +136874,7 @@ var MobileChannel = class {
|
|
|
136649
136874
|
try {
|
|
136650
136875
|
this.tunnel = await provider.open({ port: this.port, host: this.bindHost });
|
|
136651
136876
|
tunnelUrl = this.tunnel.url;
|
|
136877
|
+
server.setAllowedOrigins([...localOrigins, connectUrlOrigin(tunnelUrl)]);
|
|
136652
136878
|
this.logger?.info?.("mobile tunnel open", { provider: provider.name, url: tunnelUrl });
|
|
136653
136879
|
} catch (err) {
|
|
136654
136880
|
this.logger?.warn?.("mobile tunnel failed; using the local URL", {
|
|
@@ -137963,6 +138189,51 @@ var testProviderInput = z$1.object({
|
|
|
137963
138189
|
baseURL: z$1.string().url().describe("Vendor API base URL to probe, e.g. https://api.deepseek.com."),
|
|
137964
138190
|
keyName: z$1.string().regex(/^[A-Z][A-Z0-9_]*$/).describe("NAME of the vault secret holding the API key (e.g. DEEPSEEK_API_KEY). The key is resolved from the vault inside the tool \u2014 never ask the user for the plaintext key and never pass one as a tool argument. Have them store it first: /vault set <NAME> <key>.")
|
|
137965
138191
|
});
|
|
138192
|
+
function buildProviderAdminPluginWithApi(opts) {
|
|
138193
|
+
const { providerRegistry, configPath } = opts;
|
|
138194
|
+
const api = {
|
|
138195
|
+
configure: async (name, patch) => {
|
|
138196
|
+
const cfg = await readProvidersConfig(configPath);
|
|
138197
|
+
const entry = cfg.providers.find((p3) => p3.name === name);
|
|
138198
|
+
if (!entry) {
|
|
138199
|
+
throw new MoxxyError({
|
|
138200
|
+
code: "CONFIG_INVALID",
|
|
138201
|
+
message: `provider-admin: no stored provider named "${name}" \u2014 only runtime-registered (providers.json) providers are configurable; built-ins are code.`
|
|
138202
|
+
});
|
|
138203
|
+
}
|
|
138204
|
+
const next = {
|
|
138205
|
+
...entry,
|
|
138206
|
+
...patch.baseURL ? { baseURL: patch.baseURL } : {},
|
|
138207
|
+
...patch.defaultModel ? { defaultModel: patch.defaultModel } : {},
|
|
138208
|
+
...patch.envVar ? { envVar: patch.envVar } : {},
|
|
138209
|
+
...patch.models && patch.models.length > 0 ? { models: patch.models } : {}
|
|
138210
|
+
};
|
|
138211
|
+
if (!next.models.some((m3) => m3.id === next.defaultModel)) {
|
|
138212
|
+
throw new MoxxyError({
|
|
138213
|
+
code: "CONFIG_INVALID",
|
|
138214
|
+
message: `provider-admin: defaultModel "${next.defaultModel}" is not in the models list (${next.models.map((m3) => m3.id).join(", ")}).`
|
|
138215
|
+
});
|
|
138216
|
+
}
|
|
138217
|
+
const def = buildProviderDef(next);
|
|
138218
|
+
const hadDef = providerRegistry.list().some((p3) => p3.name === name);
|
|
138219
|
+
if (hadDef)
|
|
138220
|
+
providerRegistry.replace(def);
|
|
138221
|
+
else
|
|
138222
|
+
providerRegistry.register(def);
|
|
138223
|
+
try {
|
|
138224
|
+
await upsertStoredProvider(next, configPath);
|
|
138225
|
+
} catch (err) {
|
|
138226
|
+
const prev = buildProviderDef(entry);
|
|
138227
|
+
if (hadDef)
|
|
138228
|
+
providerRegistry.replace(prev);
|
|
138229
|
+
else
|
|
138230
|
+
providerRegistry.unregister(name);
|
|
138231
|
+
throw err;
|
|
138232
|
+
}
|
|
138233
|
+
}
|
|
138234
|
+
};
|
|
138235
|
+
return { plugin: buildProviderAdminPlugin(opts), api };
|
|
138236
|
+
}
|
|
137966
138237
|
function buildProviderAdminPlugin(opts) {
|
|
137967
138238
|
const { providerRegistry, configPath } = opts;
|
|
137968
138239
|
return definePlugin({
|
|
@@ -143953,20 +144224,25 @@ function formatCatalog(entries, emptyLabel) {
|
|
|
143953
144224
|
}
|
|
143954
144225
|
async function draftWorkflow(provider, model, intent, signal, opts = {}) {
|
|
143955
144226
|
let accumulated = "";
|
|
144227
|
+
let truncated = false;
|
|
144228
|
+
const ceiling = provider.models.find((m3) => m3.id === model)?.maxOutputTokens;
|
|
144229
|
+
const budget = Math.min(opts.maxTokens ?? DEFAULT_MAX_TOKENS, ceiling ?? Number.POSITIVE_INFINITY);
|
|
143956
144230
|
for await (const event of provider.stream({
|
|
143957
144231
|
model,
|
|
143958
144232
|
system: buildSystemPrompt(opts),
|
|
143959
144233
|
messages: [{ role: "user", content: [{ type: "text", text: `Build a workflow for: ${intent}` }] }],
|
|
143960
|
-
maxTokens:
|
|
144234
|
+
maxTokens: budget,
|
|
143961
144235
|
signal
|
|
143962
144236
|
})) {
|
|
143963
144237
|
if (event.type === "text_delta")
|
|
143964
144238
|
accumulated += event.delta;
|
|
144239
|
+
if (event.type === "message_end")
|
|
144240
|
+
truncated = event.stopReason === "max_tokens";
|
|
143965
144241
|
if (event.type === "error")
|
|
143966
144242
|
throw new Error(`workflow_create: provider error: ${event.message}`);
|
|
143967
144243
|
}
|
|
143968
144244
|
const raw = extractYamlBlock(accumulated);
|
|
143969
|
-
return { raw, parse: parseWorkflowYaml(raw) };
|
|
144245
|
+
return { raw, parse: parseWorkflowYaml(raw), truncated };
|
|
143970
144246
|
}
|
|
143971
144247
|
function extractYamlBlock(s2) {
|
|
143972
144248
|
const fence = /```(?:ya?ml)?\n([\s\S]*?)```/.exec(s2);
|
|
@@ -144045,7 +144321,7 @@ function createTool(deps) {
|
|
|
144045
144321
|
if (!drafted.parse.ok || !drafted.parse.workflow) {
|
|
144046
144322
|
throw new MoxxyError({
|
|
144047
144323
|
code: "TOOL_ERROR",
|
|
144048
|
-
message: `workflow_create: the model did not produce a valid workflow (${drafted.parse.errors.join("; ")}). Try a more specific intent.`
|
|
144324
|
+
message: drafted.truncated ? "workflow_create: the draft hit the output-token limit before the YAML was complete. Try a simpler intent, or split the workflow into smaller ones." : `workflow_create: the model did not produce a valid workflow (${drafted.parse.errors.join("; ")}). Try a more specific intent.`
|
|
144049
144325
|
});
|
|
144050
144326
|
}
|
|
144051
144327
|
const created = await deps.store.create(drafted.parse.workflow, scope);
|
|
@@ -145218,10 +145494,11 @@ function buildBuiltinsCore(args) {
|
|
|
145218
145494
|
// ~/.moxxy/providers.json; the plugin's onInit re-registers them on
|
|
145219
145495
|
// every boot. Pairs with the `add-provider` skill which walks the
|
|
145220
145496
|
// model through gathering baseURL + models + key.
|
|
145221
|
-
{
|
|
145222
|
-
|
|
145223
|
-
|
|
145224
|
-
|
|
145497
|
+
(() => {
|
|
145498
|
+
const { plugin: plugin4, api } = buildProviderAdminPluginWithApi({ providerRegistry: session.providers });
|
|
145499
|
+
session.providerAdmin = api;
|
|
145500
|
+
return { name: "@moxxy/plugin-provider-admin", plugin: plugin4 };
|
|
145501
|
+
})(),
|
|
145225
145502
|
// Admin tools (mcp_add_server, mcp_list_servers, mcp_remove_server,
|
|
145226
145503
|
// mcp_test_server) plus the boot-time lazy attach. Passing the
|
|
145227
145504
|
// session's live tool registry enables both hot-attach for runtime
|
|
@@ -145720,6 +145997,10 @@ async function activateProvider(args) {
|
|
|
145720
145997
|
} else {
|
|
145721
145998
|
for (let i2 = 0; i2 < candidates.length; i2++) {
|
|
145722
145999
|
const candidate = candidates[i2];
|
|
146000
|
+
if (!session.providers.isEnabled(candidate)) {
|
|
146001
|
+
logger.warn("skipping disabled provider", { provider: candidate });
|
|
146002
|
+
continue;
|
|
146003
|
+
}
|
|
145723
146004
|
const interactive = i2 === 0 && !skipKeyPrompt && process.stdin.isTTY === true;
|
|
145724
146005
|
try {
|
|
145725
146006
|
const resolved = await resolveProviderCredentials(candidate, vault, {
|
|
@@ -145928,6 +146209,13 @@ async function setupSessionWithConfig(opts) {
|
|
|
145928
146209
|
});
|
|
145929
146210
|
await selectEmbedder(session, rawConfig.embeddings, logger);
|
|
145930
146211
|
for (const iso2 of session.isolators.list()) security.registry.register(iso2);
|
|
146212
|
+
try {
|
|
146213
|
+
const bootPrefs = await loadPreferences();
|
|
146214
|
+
for (const name of bootPrefs.disabledProviders ?? []) {
|
|
146215
|
+
session.providers.setEnabled(name, false);
|
|
146216
|
+
}
|
|
146217
|
+
} catch {
|
|
146218
|
+
}
|
|
145931
146219
|
const { credentialResolver } = await activateProvider({
|
|
145932
146220
|
session,
|
|
145933
146221
|
config,
|