miladyai 2.0.0-alpha.27
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/_virtual/_rolldown/runtime.js +7 -0
- package/dist/actions/emote.js +64 -0
- package/dist/actions/restart.js +81 -0
- package/dist/actions/send-message.js +152 -0
- package/dist/agent-admin-routes.js +82 -0
- package/dist/agent-lifecycle-routes.js +79 -0
- package/dist/agent-transfer-routes.js +102 -0
- package/dist/api/agent-admin-routes.js +82 -0
- package/dist/api/agent-lifecycle-routes.js +79 -0
- package/dist/api/agent-transfer-routes.js +102 -0
- package/dist/api/apps-hyperscape-routes.js +58 -0
- package/dist/api/apps-routes.js +114 -0
- package/dist/api/auth-routes.js +56 -0
- package/dist/api/autonomy-routes.js +44 -0
- package/dist/api/bug-report-routes.js +111 -0
- package/dist/api/character-routes.js +195 -0
- package/dist/api/cloud-routes.js +330 -0
- package/dist/api/cloud-status-routes.js +155 -0
- package/dist/api/compat-utils.js +111 -0
- package/dist/api/database.js +735 -0
- package/dist/api/diagnostics-routes.js +205 -0
- package/dist/api/drop-service.js +134 -0
- package/dist/api/early-logs.js +86 -0
- package/dist/api/http-helpers.js +131 -0
- package/dist/api/knowledge-routes.js +534 -0
- package/dist/api/memory-bounds.js +71 -0
- package/dist/api/models-routes.js +28 -0
- package/dist/api/og-tracker.js +36 -0
- package/dist/api/permissions-routes.js +109 -0
- package/dist/api/plugin-validation.js +198 -0
- package/dist/api/provider-switch-config.js +41 -0
- package/dist/api/registry-routes.js +86 -0
- package/dist/api/registry-service.js +164 -0
- package/dist/api/sandbox-routes.js +1112 -0
- package/dist/api/server.js +7949 -0
- package/dist/api/subscription-routes.js +172 -0
- package/dist/api/terminal-run-limits.js +24 -0
- package/dist/api/training-routes.js +158 -0
- package/dist/api/trajectory-routes.js +300 -0
- package/dist/api/trigger-routes.js +246 -0
- package/dist/api/twitter-verify.js +134 -0
- package/dist/api/tx-service.js +108 -0
- package/dist/api/wallet-routes.js +266 -0
- package/dist/api/wallet.js +568 -0
- package/dist/api/whatsapp-routes.js +182 -0
- package/dist/api/zip-utils.js +109 -0
- package/dist/apps-hyperscape-routes.js +58 -0
- package/dist/apps-routes.js +114 -0
- package/dist/ascii.js +20 -0
- package/dist/auth/anthropic.js +44 -0
- package/dist/auth/apply-stealth.js +41 -0
- package/dist/auth/claude-code-stealth.js +78 -0
- package/dist/auth/credentials.js +156 -0
- package/dist/auth/index.js +5 -0
- package/dist/auth/openai-codex.js +66 -0
- package/dist/auth/types.js +9 -0
- package/dist/auth-routes.js +56 -0
- package/dist/autonomy-routes.js +44 -0
- package/dist/bug-report-routes.js +111 -0
- package/dist/build-info.json +6 -0
- package/dist/character-routes.js +195 -0
- package/dist/cli/argv.js +63 -0
- package/dist/cli/banner.js +34 -0
- package/dist/cli/cli-name.js +21 -0
- package/dist/cli/cli-utils.js +16 -0
- package/dist/cli/git-commit.js +78 -0
- package/dist/cli/parse-duration.js +15 -0
- package/dist/cli/plugins-cli.js +590 -0
- package/dist/cli/profile-utils.js +9 -0
- package/dist/cli/profile.js +95 -0
- package/dist/cli/program/build-program.js +17 -0
- package/dist/cli/program/command-registry.js +23 -0
- package/dist/cli/program/help.js +47 -0
- package/dist/cli/program/preaction.js +33 -0
- package/dist/cli/program/register.config.js +106 -0
- package/dist/cli/program/register.configure.js +20 -0
- package/dist/cli/program/register.dashboard.js +124 -0
- package/dist/cli/program/register.models.js +23 -0
- package/dist/cli/program/register.setup.js +36 -0
- package/dist/cli/program/register.start.js +22 -0
- package/dist/cli/program/register.subclis.js +70 -0
- package/dist/cli/program/register.tui.js +163 -0
- package/dist/cli/program/register.update.js +154 -0
- package/dist/cli/program.js +3 -0
- package/dist/cli/run-main.js +37 -0
- package/dist/cli/version.js +7 -0
- package/dist/cloud/validate-url.js +93 -0
- package/dist/cloud-routes.js +330 -0
- package/dist/cloud-status-routes.js +155 -0
- package/dist/compat-utils.js +111 -0
- package/dist/config/config.js +69 -0
- package/dist/config/env-vars.js +19 -0
- package/dist/config/includes.js +121 -0
- package/dist/config/object-utils.js +7 -0
- package/dist/config/paths.js +38 -0
- package/dist/config/plugin-auto-enable.js +231 -0
- package/dist/config/schema.js +864 -0
- package/dist/config/telegram-custom-commands.js +76 -0
- package/dist/config/zod-schema.agent-runtime.js +519 -0
- package/dist/config/zod-schema.core.js +538 -0
- package/dist/config/zod-schema.hooks.js +103 -0
- package/dist/config/zod-schema.js +488 -0
- package/dist/config/zod-schema.providers-core.js +785 -0
- package/dist/config/zod-schema.session.js +73 -0
- package/dist/core-plugins.js +37 -0
- package/dist/custom-actions.js +250 -0
- package/dist/database.js +735 -0
- package/dist/diagnostics/integration-observability.js +57 -0
- package/dist/diagnostics-routes.js +205 -0
- package/dist/drop-service.js +134 -0
- package/dist/early-logs.js +24 -0
- package/dist/eliza.js +2061 -0
- package/dist/emotes/catalog.js +271 -0
- package/dist/entry.js +40 -0
- package/dist/hooks/discovery.js +167 -0
- package/dist/hooks/eligibility.js +64 -0
- package/dist/hooks/index.js +4 -0
- package/dist/hooks/loader.js +147 -0
- package/dist/hooks/registry.js +55 -0
- package/dist/http-helpers.js +131 -0
- package/dist/index.js +49 -0
- package/dist/knowledge-routes.js +534 -0
- package/dist/memory-bounds.js +71 -0
- package/dist/milady-plugin.js +90 -0
- package/dist/models-routes.js +28 -0
- package/dist/onboarding-names.js +78 -0
- package/dist/onboarding-presets.js +922 -0
- package/dist/package.json +1 -0
- package/dist/permissions-routes.js +109 -0
- package/dist/plugin-validation.js +107 -0
- package/dist/plugins/whatsapp/actions.js +91 -0
- package/dist/plugins/whatsapp/index.js +16 -0
- package/dist/plugins/whatsapp/service.js +270 -0
- package/dist/provider-switch-config.js +41 -0
- package/dist/providers/admin-trust.js +46 -0
- package/dist/providers/autonomous-state.js +101 -0
- package/dist/providers/session-bridge.js +86 -0
- package/dist/providers/session-utils.js +36 -0
- package/dist/providers/simple-mode.js +50 -0
- package/dist/providers/ui-catalog.js +15 -0
- package/dist/providers/workspace-provider.js +93 -0
- package/dist/providers/workspace.js +348 -0
- package/dist/registry-routes.js +86 -0
- package/dist/registry-service.js +164 -0
- package/dist/restart.js +40 -0
- package/dist/runtime/core-plugins.js +37 -0
- package/dist/runtime/custom-actions.js +250 -0
- package/dist/runtime/eliza.js +2061 -0
- package/dist/runtime/embedding-manager-support.js +185 -0
- package/dist/runtime/embedding-manager.js +193 -0
- package/dist/runtime/embedding-presets.js +54 -0
- package/dist/runtime/embedding-state.js +8 -0
- package/dist/runtime/milady-plugin.js +90 -0
- package/dist/runtime/onboarding-names.js +78 -0
- package/dist/runtime/restart.js +40 -0
- package/dist/runtime/version.js +7 -0
- package/dist/sandbox-routes.js +1112 -0
- package/dist/security/audit-log.js +149 -0
- package/dist/security/network-policy.js +70 -0
- package/dist/server.js +7949 -0
- package/dist/services/agent-export.js +559 -0
- package/dist/services/app-manager.js +389 -0
- package/dist/services/browser-capture.js +86 -0
- package/dist/services/fallback-training-service.js +128 -0
- package/dist/services/mcp-marketplace.js +134 -0
- package/dist/services/plugin-installer.js +396 -0
- package/dist/services/plugin-manager-types.js +15 -0
- package/dist/services/registry-client-app-meta.js +144 -0
- package/dist/services/registry-client-endpoints.js +166 -0
- package/dist/services/registry-client-local.js +271 -0
- package/dist/services/registry-client-network.js +93 -0
- package/dist/services/registry-client-queries.js +70 -0
- package/dist/services/registry-client.js +157 -0
- package/dist/services/sandbox-engine.js +511 -0
- package/dist/services/sandbox-manager.js +297 -0
- package/dist/services/self-updater.js +175 -0
- package/dist/services/skill-catalog-client.js +119 -0
- package/dist/services/skill-marketplace.js +521 -0
- package/dist/services/stream-manager.js +236 -0
- package/dist/services/update-checker.js +121 -0
- package/dist/services/update-notifier.js +29 -0
- package/dist/services/version-compat.js +78 -0
- package/dist/services/whatsapp-pairing.js +196 -0
- package/dist/shared/ui-catalog-prompt.js +728 -0
- package/dist/subscription-routes.js +172 -0
- package/dist/terminal/links.js +19 -0
- package/dist/terminal/palette.js +14 -0
- package/dist/terminal/theme.js +25 -0
- package/dist/terminal-run-limits.js +24 -0
- package/dist/training-routes.js +158 -0
- package/dist/trajectory-routes.js +300 -0
- package/dist/trigger-routes.js +246 -0
- package/dist/triggers/action.js +218 -0
- package/dist/triggers/runtime.js +281 -0
- package/dist/triggers/scheduling.js +295 -0
- package/dist/triggers/types.js +5 -0
- package/dist/tui/components/assistant-message.js +76 -0
- package/dist/tui/components/chat-editor.js +34 -0
- package/dist/tui/components/embeddings-overlay.js +46 -0
- package/dist/tui/components/footer.js +60 -0
- package/dist/tui/components/index.js +15 -0
- package/dist/tui/components/modal-frame.js +45 -0
- package/dist/tui/components/modal-style.js +15 -0
- package/dist/tui/components/model-selector.js +70 -0
- package/dist/tui/components/pinned-chat-layout.js +46 -0
- package/dist/tui/components/plugins-endpoints-tab.js +196 -0
- package/dist/tui/components/plugins-installed-tab-view.js +69 -0
- package/dist/tui/components/plugins-installed-tab.js +319 -0
- package/dist/tui/components/plugins-overlay-catalog.js +81 -0
- package/dist/tui/components/plugins-overlay-data-api.js +21 -0
- package/dist/tui/components/plugins-overlay-data-shared.js +20 -0
- package/dist/tui/components/plugins-overlay-data.js +323 -0
- package/dist/tui/components/plugins-overlay.js +117 -0
- package/dist/tui/components/plugins-store-tab.js +148 -0
- package/dist/tui/components/settings-overlay.js +61 -0
- package/dist/tui/components/status-bar.js +64 -0
- package/dist/tui/components/tool-execution.js +68 -0
- package/dist/tui/components/user-message.js +22 -0
- package/dist/tui/eliza-tui-bridge.js +606 -0
- package/dist/tui/index.js +370 -0
- package/dist/tui/modal-presets.js +33 -0
- package/dist/tui/model-spec.js +46 -0
- package/dist/tui/sse-parser.js +78 -0
- package/dist/tui/theme.js +110 -0
- package/dist/tui/titlebar-spinner.js +62 -0
- package/dist/tui/tui-app.js +311 -0
- package/dist/tui/ws-client.js +215 -0
- package/dist/twitter-verify.js +134 -0
- package/dist/tx-service.js +108 -0
- package/dist/utils/exec-safety.js +17 -0
- package/dist/utils/globals.js +20 -0
- package/dist/utils/milady-root.js +61 -0
- package/dist/utils/number-parsing.js +37 -0
- package/dist/version-resolver.js +37 -0
- package/dist/version.js +7 -0
- package/dist/wallet-routes.js +266 -0
- package/dist/wallet.js +568 -0
- package/dist/whatsapp-routes.js +182 -0
- package/dist/zip-utils.js +109 -0
- package/milady.mjs +14 -0
- package/package.json +111 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { SUBSCRIPTION_PROVIDER_MAP } from "./types.js";
|
|
2
|
+
import { refreshAnthropicToken } from "./anthropic.js";
|
|
3
|
+
import { refreshCodexToken } from "./openai-codex.js";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { logger } from "@elizaos/core";
|
|
7
|
+
import fs from "node:fs";
|
|
8
|
+
|
|
9
|
+
//#region src/auth/credentials.ts
|
|
10
|
+
/**
|
|
11
|
+
* Credential storage and token refresh for subscription providers.
|
|
12
|
+
*
|
|
13
|
+
* Stores OAuth credentials in ~/.milady/auth/ as JSON files.
|
|
14
|
+
*/
|
|
15
|
+
const AUTH_DIR = path.join(process.env.MILADY_HOME || path.join(os.homedir(), ".milady"), "auth");
|
|
16
|
+
/** Buffer before expiry to trigger refresh (5 minutes) */
|
|
17
|
+
const REFRESH_BUFFER_MS = 300 * 1e3;
|
|
18
|
+
function ensureAuthDir() {
|
|
19
|
+
if (!fs.existsSync(AUTH_DIR)) fs.mkdirSync(AUTH_DIR, {
|
|
20
|
+
recursive: true,
|
|
21
|
+
mode: 448
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
function credentialPath(provider) {
|
|
25
|
+
return path.join(AUTH_DIR, `${provider}.json`);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Save credentials for a provider.
|
|
29
|
+
*/
|
|
30
|
+
function saveCredentials(provider, credentials) {
|
|
31
|
+
ensureAuthDir();
|
|
32
|
+
const stored = {
|
|
33
|
+
provider,
|
|
34
|
+
credentials,
|
|
35
|
+
createdAt: Date.now(),
|
|
36
|
+
updatedAt: Date.now()
|
|
37
|
+
};
|
|
38
|
+
fs.writeFileSync(credentialPath(provider), JSON.stringify(stored, null, 2), {
|
|
39
|
+
encoding: "utf-8",
|
|
40
|
+
mode: 384
|
|
41
|
+
});
|
|
42
|
+
logger.info(`[auth] Saved ${provider} credentials`);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Load stored credentials for a provider.
|
|
46
|
+
*/
|
|
47
|
+
function loadCredentials(provider) {
|
|
48
|
+
const filePath = credentialPath(provider);
|
|
49
|
+
try {
|
|
50
|
+
const data = fs.readFileSync(filePath, "utf-8");
|
|
51
|
+
return JSON.parse(data);
|
|
52
|
+
} catch (err) {
|
|
53
|
+
if (err.code === "ENOENT") return null;
|
|
54
|
+
throw err;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Delete stored credentials for a provider.
|
|
59
|
+
*/
|
|
60
|
+
function deleteCredentials(provider) {
|
|
61
|
+
const filePath = credentialPath(provider);
|
|
62
|
+
try {
|
|
63
|
+
fs.unlinkSync(filePath);
|
|
64
|
+
logger.info(`[auth] Deleted ${provider} credentials`);
|
|
65
|
+
} catch (err) {
|
|
66
|
+
if (err.code !== "ENOENT") throw err;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Get a valid access token, refreshing if needed.
|
|
71
|
+
* Returns null if no credentials stored or refresh fails.
|
|
72
|
+
*/
|
|
73
|
+
async function getAccessToken(provider) {
|
|
74
|
+
const stored = loadCredentials(provider);
|
|
75
|
+
if (!stored) return null;
|
|
76
|
+
const { credentials } = stored;
|
|
77
|
+
if (credentials.expires > Date.now() + REFRESH_BUFFER_MS) return credentials.access;
|
|
78
|
+
logger.info(`[auth] Refreshing ${provider} token...`);
|
|
79
|
+
try {
|
|
80
|
+
let refreshed;
|
|
81
|
+
if (provider === "anthropic-subscription") refreshed = await refreshAnthropicToken(credentials.refresh);
|
|
82
|
+
else if (provider === "openai-codex") refreshed = await refreshCodexToken(credentials.refresh);
|
|
83
|
+
else {
|
|
84
|
+
logger.error(`[auth] Unknown provider: ${provider}`);
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
saveCredentials(provider, refreshed);
|
|
88
|
+
return refreshed.access;
|
|
89
|
+
} catch (err) {
|
|
90
|
+
logger.error(`[auth] Failed to refresh ${provider} token: ${err}`);
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Get all configured subscription providers and their status.
|
|
96
|
+
*/
|
|
97
|
+
function getSubscriptionStatus() {
|
|
98
|
+
return ["anthropic-subscription", "openai-codex"].map((provider) => {
|
|
99
|
+
const stored = loadCredentials(provider);
|
|
100
|
+
return {
|
|
101
|
+
provider,
|
|
102
|
+
configured: stored !== null,
|
|
103
|
+
valid: stored ? stored.credentials.expires > Date.now() : false,
|
|
104
|
+
expiresAt: stored?.credentials.expires ?? null
|
|
105
|
+
};
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Apply subscription credentials to the environment.
|
|
110
|
+
* Called at startup to make credentials available to ElizaOS plugins.
|
|
111
|
+
*
|
|
112
|
+
* When a `config` is provided and the active subscription provider has
|
|
113
|
+
* credentials, `model.primary` is auto-set so the user doesn't need to
|
|
114
|
+
* configure it manually.
|
|
115
|
+
*/
|
|
116
|
+
async function applySubscriptionCredentials(config) {
|
|
117
|
+
const anthropicToken = await getAccessToken("anthropic-subscription");
|
|
118
|
+
if (anthropicToken) {
|
|
119
|
+
process.env.ANTHROPIC_API_KEY = anthropicToken;
|
|
120
|
+
logger.info("[auth] Applied Anthropic subscription credentials to environment");
|
|
121
|
+
try {
|
|
122
|
+
const { applyClaudeCodeStealth } = await import("./apply-stealth.js");
|
|
123
|
+
applyClaudeCodeStealth();
|
|
124
|
+
} catch (err) {
|
|
125
|
+
logger.warn(`[auth] Failed to apply Claude stealth: ${err instanceof Error ? err.message : err}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const codexToken = await getAccessToken("openai-codex");
|
|
129
|
+
if (codexToken) {
|
|
130
|
+
process.env.OPENAI_API_KEY = codexToken;
|
|
131
|
+
logger.info("[auth] Applied OpenAI Codex subscription credentials to environment");
|
|
132
|
+
try {
|
|
133
|
+
const { applyOpenAICodexStealth } = await import("./apply-stealth.js");
|
|
134
|
+
await applyOpenAICodexStealth();
|
|
135
|
+
} catch (err) {
|
|
136
|
+
logger.warn(`[auth] Failed to apply OpenAI Codex stealth: ${err instanceof Error ? err.message : err}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (config?.agents?.defaults) {
|
|
140
|
+
const defaults = config.agents.defaults;
|
|
141
|
+
const provider = defaults.subscriptionProvider;
|
|
142
|
+
const modelId = provider ? SUBSCRIPTION_PROVIDER_MAP[provider] : void 0;
|
|
143
|
+
if (modelId) {
|
|
144
|
+
if (!defaults.model) {
|
|
145
|
+
defaults.model = { primary: modelId };
|
|
146
|
+
logger.info(`[auth] Auto-set model.primary to "${modelId}" from subscription provider`);
|
|
147
|
+
} else if (!defaults.model.primary) {
|
|
148
|
+
defaults.model.primary = modelId;
|
|
149
|
+
logger.info(`[auth] Auto-set model.primary to "${modelId}" from subscription provider`);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
//#endregion
|
|
156
|
+
export { applySubscriptionCredentials, deleteCredentials, getAccessToken, getSubscriptionStatus, loadCredentials, saveCredentials };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { refreshAnthropicToken, startAnthropicLogin } from "./anthropic.js";
|
|
2
|
+
import { refreshCodexToken, startCodexLogin } from "./openai-codex.js";
|
|
3
|
+
import { applySubscriptionCredentials, deleteCredentials, getAccessToken, getSubscriptionStatus, loadCredentials, saveCredentials } from "./credentials.js";
|
|
4
|
+
|
|
5
|
+
export { applySubscriptionCredentials, deleteCredentials, getSubscriptionStatus, saveCredentials, startAnthropicLogin, startCodexLogin };
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { loginOpenAICodex, refreshOpenAICodexToken } from "@mariozechner/pi-ai";
|
|
2
|
+
|
|
3
|
+
//#region src/auth/openai-codex.ts
|
|
4
|
+
/**
|
|
5
|
+
* OpenAI Codex (ChatGPT Plus/Pro subscription) OAuth flow
|
|
6
|
+
*
|
|
7
|
+
* Wraps @mariozechner/pi-ai's loginOpenAICodex for server-side use.
|
|
8
|
+
* Handles local callback server + manual code paste fallback.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Start the OpenAI Codex OAuth flow.
|
|
12
|
+
* Starts a local callback server on port 1455 and returns the auth URL.
|
|
13
|
+
*/
|
|
14
|
+
function startCodexLogin() {
|
|
15
|
+
return new Promise((resolveFlow, rejectFlow) => {
|
|
16
|
+
let authUrl = "";
|
|
17
|
+
let flowState = "";
|
|
18
|
+
let resolveManual = null;
|
|
19
|
+
let closeServer = null;
|
|
20
|
+
let credentials;
|
|
21
|
+
const manualPromise = new Promise((resolve) => {
|
|
22
|
+
resolveManual = resolve;
|
|
23
|
+
});
|
|
24
|
+
try {
|
|
25
|
+
credentials = loginOpenAICodex({
|
|
26
|
+
onAuth: ({ url }) => {
|
|
27
|
+
authUrl = url;
|
|
28
|
+
try {
|
|
29
|
+
flowState = new URL(url).searchParams.get("state") || "";
|
|
30
|
+
} catch {}
|
|
31
|
+
resolveFlow({
|
|
32
|
+
get authUrl() {
|
|
33
|
+
return authUrl;
|
|
34
|
+
},
|
|
35
|
+
state: flowState,
|
|
36
|
+
submitCode: (code) => resolveManual?.(code),
|
|
37
|
+
credentials,
|
|
38
|
+
close: () => closeServer?.()
|
|
39
|
+
});
|
|
40
|
+
},
|
|
41
|
+
onPrompt: async () => {
|
|
42
|
+
return manualPromise;
|
|
43
|
+
},
|
|
44
|
+
onManualCodeInput: () => manualPromise,
|
|
45
|
+
onProgress: () => {},
|
|
46
|
+
originator: "milady"
|
|
47
|
+
});
|
|
48
|
+
credentials.catch(() => {});
|
|
49
|
+
} catch (err) {
|
|
50
|
+
rejectFlow(err);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
closeServer = () => {
|
|
54
|
+
resolveManual?.("");
|
|
55
|
+
};
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Refresh an expired OpenAI Codex token.
|
|
60
|
+
*/
|
|
61
|
+
async function refreshCodexToken(refreshToken) {
|
|
62
|
+
return refreshOpenAICodexToken(refreshToken);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
//#endregion
|
|
66
|
+
export { refreshCodexToken, startCodexLogin };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
//#region src/auth/types.ts
|
|
2
|
+
/** Maps subscription provider IDs to their model provider short names. */
|
|
3
|
+
const SUBSCRIPTION_PROVIDER_MAP = {
|
|
4
|
+
"anthropic-subscription": "anthropic",
|
|
5
|
+
"openai-codex": "openai"
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
//#endregion
|
|
9
|
+
export { SUBSCRIPTION_PROVIDER_MAP };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
|
|
3
|
+
//#region src/api/auth-routes.ts
|
|
4
|
+
async function handleAuthRoutes(ctx) {
|
|
5
|
+
const { req, res, method, pathname, readJsonBody, json, error, pairingEnabled, ensurePairingCode, normalizePairingCode, rateLimitPairing, getPairingExpiresAt, clearPairing } = ctx;
|
|
6
|
+
if (!pathname.startsWith("/api/auth/")) return false;
|
|
7
|
+
if (method === "GET" && pathname === "/api/auth/status") {
|
|
8
|
+
const required = Boolean(process.env.MILADY_API_TOKEN?.trim());
|
|
9
|
+
const enabled = pairingEnabled();
|
|
10
|
+
if (enabled) ensurePairingCode();
|
|
11
|
+
json(res, {
|
|
12
|
+
required,
|
|
13
|
+
pairingEnabled: enabled,
|
|
14
|
+
expiresAt: enabled ? getPairingExpiresAt() : null
|
|
15
|
+
});
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
if (method === "POST" && pathname === "/api/auth/pair") {
|
|
19
|
+
const body = await readJsonBody(req, res);
|
|
20
|
+
if (!body) return true;
|
|
21
|
+
const token = process.env.MILADY_API_TOKEN?.trim();
|
|
22
|
+
if (!token) {
|
|
23
|
+
error(res, "Pairing not enabled", 400);
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
if (!pairingEnabled()) {
|
|
27
|
+
error(res, "Pairing disabled", 403);
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
if (!rateLimitPairing(req.socket.remoteAddress ?? null)) {
|
|
31
|
+
error(res, "Too many attempts. Try again later.", 429);
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
const provided = normalizePairingCode(body.code ?? "");
|
|
35
|
+
const current = ensurePairingCode();
|
|
36
|
+
if (!current || Date.now() > getPairingExpiresAt()) {
|
|
37
|
+
ensurePairingCode();
|
|
38
|
+
error(res, "Pairing code expired. Check server logs for a new code.", 410);
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
const expected = normalizePairingCode(current);
|
|
42
|
+
const a = Buffer.from(expected, "utf8");
|
|
43
|
+
const b = Buffer.from(provided, "utf8");
|
|
44
|
+
if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
|
|
45
|
+
error(res, "Invalid pairing code", 403);
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
clearPairing();
|
|
49
|
+
json(res, { token });
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
//#endregion
|
|
56
|
+
export { handleAuthRoutes };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
//#region src/api/autonomy-routes.ts
|
|
2
|
+
/** Helper to retrieve the AutonomyService from a runtime (may be null). */
|
|
3
|
+
function getAutonomySvc(runtime) {
|
|
4
|
+
if (!runtime) return null;
|
|
5
|
+
return runtime.getService("AUTONOMY");
|
|
6
|
+
}
|
|
7
|
+
function getAutonomyState(runtime) {
|
|
8
|
+
const svc = getAutonomySvc(runtime);
|
|
9
|
+
const statusEnabled = svc?.getStatus?.().enabled;
|
|
10
|
+
const runtimeEnabled = runtime?.enableAutonomy === true;
|
|
11
|
+
return {
|
|
12
|
+
enabled: typeof statusEnabled === "boolean" ? statusEnabled : runtimeEnabled || Boolean(svc),
|
|
13
|
+
thinking: svc?.isLoopRunning() ?? false
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
async function handleAutonomyRoutes(ctx) {
|
|
17
|
+
const { req, res, method, pathname, runtime, readJsonBody, json } = ctx;
|
|
18
|
+
if (method === "POST" && pathname === "/api/agent/autonomy") {
|
|
19
|
+
const body = await readJsonBody(req, res);
|
|
20
|
+
if (!body) return true;
|
|
21
|
+
const svc = getAutonomySvc(runtime);
|
|
22
|
+
if (typeof body.enabled === "boolean" && svc) if (body.enabled) await svc.enableAutonomy();
|
|
23
|
+
else await svc.disableAutonomy();
|
|
24
|
+
const autonomy = getAutonomyState(runtime);
|
|
25
|
+
json(res, {
|
|
26
|
+
ok: true,
|
|
27
|
+
autonomy: autonomy.enabled,
|
|
28
|
+
thinking: autonomy.thinking
|
|
29
|
+
});
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
if (method === "GET" && pathname === "/api/agent/autonomy") {
|
|
33
|
+
const autonomy = getAutonomyState(runtime);
|
|
34
|
+
json(res, {
|
|
35
|
+
enabled: autonomy.enabled,
|
|
36
|
+
thinking: autonomy.thinking
|
|
37
|
+
});
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
//#endregion
|
|
44
|
+
export { getAutonomyState, getAutonomySvc, handleAutonomyRoutes };
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { sweepExpiredEntries } from "./memory-bounds.js";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
|
|
4
|
+
//#region src/api/bug-report-routes.ts
|
|
5
|
+
const BUG_REPORT_REPO = "milady-ai/milady";
|
|
6
|
+
const GITHUB_ISSUES_URL = `https://api.github.com/repos/${BUG_REPORT_REPO}/issues`;
|
|
7
|
+
const GITHUB_NEW_ISSUE_URL = `https://github.com/${BUG_REPORT_REPO}/issues/new?template=bug_report.yml`;
|
|
8
|
+
const BUG_REPORT_WINDOW_MS = 600 * 1e3;
|
|
9
|
+
const BUG_REPORT_MAX_SUBMISSIONS = 5;
|
|
10
|
+
const bugReportAttempts = /* @__PURE__ */ new Map();
|
|
11
|
+
function rateLimitBugReport(ip) {
|
|
12
|
+
const key = ip ?? "unknown";
|
|
13
|
+
const now = Date.now();
|
|
14
|
+
sweepExpiredEntries(bugReportAttempts, now, 100);
|
|
15
|
+
const current = bugReportAttempts.get(key);
|
|
16
|
+
if (!current || now > current.resetAt) {
|
|
17
|
+
bugReportAttempts.set(key, {
|
|
18
|
+
count: 1,
|
|
19
|
+
resetAt: now + BUG_REPORT_WINDOW_MS
|
|
20
|
+
});
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
if (current.count >= BUG_REPORT_MAX_SUBMISSIONS) return false;
|
|
24
|
+
current.count += 1;
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Strip HTML tags and limit length to prevent markdown injection.
|
|
29
|
+
* GitHub's renderer already sanitizes HTML, but we defensively strip
|
|
30
|
+
* tags and cap field length to reduce abuse surface.
|
|
31
|
+
*/
|
|
32
|
+
function sanitize(input, maxLen = 1e4) {
|
|
33
|
+
return input.replace(/<[^>]*>/g, "").slice(0, maxLen);
|
|
34
|
+
}
|
|
35
|
+
function formatIssueBody(body) {
|
|
36
|
+
const sections = [];
|
|
37
|
+
sections.push(`### Description\n\n${sanitize(body.description)}`);
|
|
38
|
+
sections.push(`### Steps to Reproduce\n\n${sanitize(body.stepsToReproduce)}`);
|
|
39
|
+
if (body.expectedBehavior) sections.push(`### Expected Behavior\n\n${sanitize(body.expectedBehavior)}`);
|
|
40
|
+
if (body.actualBehavior) sections.push(`### Actual Behavior\n\n${sanitize(body.actualBehavior)}`);
|
|
41
|
+
if (body.environment) sections.push(`### Environment\n\n${sanitize(body.environment, 200)}`);
|
|
42
|
+
if (body.nodeVersion) sections.push(`### Node Version\n\n${sanitize(body.nodeVersion, 200)}`);
|
|
43
|
+
if (body.modelProvider) sections.push(`### Model Provider\n\n${sanitize(body.modelProvider, 200)}`);
|
|
44
|
+
if (body.logs) sections.push(`### Logs\n\n\`\`\`\n${sanitize(body.logs, 5e4)}\n\`\`\``);
|
|
45
|
+
return sections.join("\n\n");
|
|
46
|
+
}
|
|
47
|
+
async function handleBugReportRoutes(ctx) {
|
|
48
|
+
const { req, res, method, pathname, json, error, readJsonBody } = ctx;
|
|
49
|
+
if (method === "GET" && pathname === "/api/bug-report/info") {
|
|
50
|
+
json(res, {
|
|
51
|
+
nodeVersion: process.version,
|
|
52
|
+
platform: os.platform()
|
|
53
|
+
});
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
if (method === "POST" && pathname === "/api/bug-report") {
|
|
57
|
+
if (!rateLimitBugReport(req.socket.remoteAddress ?? null)) {
|
|
58
|
+
error(res, "Too many bug reports. Try again later.", 429);
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
const body = await readJsonBody(req, res);
|
|
62
|
+
if (!body) return true;
|
|
63
|
+
if (!body.description?.trim() || !body.stepsToReproduce?.trim()) {
|
|
64
|
+
error(res, "description and stepsToReproduce are required", 400);
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
const githubToken = process.env.GITHUB_TOKEN;
|
|
68
|
+
if (!githubToken) {
|
|
69
|
+
json(res, { fallback: GITHUB_NEW_ISSUE_URL });
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
const sanitizedTitle = sanitize(body.description, 80).replace(/[\r\n]+/g, " ");
|
|
74
|
+
const issueBody = formatIssueBody(body);
|
|
75
|
+
const issueRes = await fetch(GITHUB_ISSUES_URL, {
|
|
76
|
+
method: "POST",
|
|
77
|
+
headers: {
|
|
78
|
+
Authorization: `Bearer ${githubToken}`,
|
|
79
|
+
Accept: "application/vnd.github.v3+json",
|
|
80
|
+
"Content-Type": "application/json"
|
|
81
|
+
},
|
|
82
|
+
body: JSON.stringify({
|
|
83
|
+
title: `[Bug] ${sanitizedTitle}`,
|
|
84
|
+
body: issueBody,
|
|
85
|
+
labels: [
|
|
86
|
+
"bug",
|
|
87
|
+
"triage",
|
|
88
|
+
"user-reported"
|
|
89
|
+
]
|
|
90
|
+
})
|
|
91
|
+
});
|
|
92
|
+
if (!issueRes.ok) {
|
|
93
|
+
error(res, `GitHub API error (${issueRes.status})`, 502);
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
const url = (await issueRes.json()).html_url;
|
|
97
|
+
if (typeof url !== "string" || !url.startsWith(`https://github.com/${BUG_REPORT_REPO}/issues/`)) {
|
|
98
|
+
error(res, "Unexpected response from GitHub API", 502);
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
json(res, { url });
|
|
102
|
+
} catch (_err) {
|
|
103
|
+
error(res, "Failed to create GitHub issue", 500);
|
|
104
|
+
}
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
//#endregion
|
|
111
|
+
export { handleBugReportRoutes };
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { CharacterSchema } from "./config/zod-schema.js";
|
|
2
|
+
import { ModelType, logger } from "@elizaos/core";
|
|
3
|
+
|
|
4
|
+
//#region src/api/character-routes.ts
|
|
5
|
+
function buildCharacterSummary(ctx) {
|
|
6
|
+
return [
|
|
7
|
+
ctx.name ? `Name: ${ctx.name}` : "",
|
|
8
|
+
ctx.system ? `System prompt: ${ctx.system}` : "",
|
|
9
|
+
ctx.bio ? `Bio: ${ctx.bio}` : "",
|
|
10
|
+
ctx.style?.all?.length ? `Style rules: ${ctx.style.all.join("; ")}` : ""
|
|
11
|
+
].filter(Boolean).join("\n");
|
|
12
|
+
}
|
|
13
|
+
function buildGeneratePrompt(field, context, mode) {
|
|
14
|
+
const charSummary = buildCharacterSummary(context);
|
|
15
|
+
if (field === "bio") return `Given this character:\n${charSummary}\n\nWrite a concise, compelling bio for this character (3-4 short paragraphs, one per line). Just output the bio lines, nothing else. Match the character's voice and personality.`;
|
|
16
|
+
if (field === "style") return `Given this character:\n${charSummary}${mode === "append" && context.style?.all?.length ? `\nExisting style rules (add to these, don't repeat):\n${context.style.all.join("\n")}` : ""}\n\nGenerate 4-6 communication style rules for this character. Output a JSON object with keys "all", "chat", "post", each containing an array of short rule strings. Just output the JSON, nothing else.`;
|
|
17
|
+
if (field === "chatExamples") return `Given this character:\n${charSummary}\n\nGenerate 3 example chat conversations showing how this character responds. Output a JSON array where each element is an array of message objects like [{"user":"{{user1}}","content":{"text":"..."}},{"user":"{{agentName}}","content":{"text":"..."}}]. Just output the JSON array, nothing else.`;
|
|
18
|
+
return `Given this character:\n${charSummary}${mode === "append" && context.postExamples?.length ? `\nExisting posts (add new ones, don't repeat):\n${context.postExamples.join("\n")}` : ""}\n\nGenerate 3-5 example social media posts this character would write. Output a JSON array of strings. Just output the JSON array, nothing else.`;
|
|
19
|
+
}
|
|
20
|
+
const CHARACTER_SCHEMA_FIELDS = [
|
|
21
|
+
{
|
|
22
|
+
key: "name",
|
|
23
|
+
type: "string",
|
|
24
|
+
label: "Name",
|
|
25
|
+
description: "Agent display name",
|
|
26
|
+
maxLength: 100
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
key: "username",
|
|
30
|
+
type: "string",
|
|
31
|
+
label: "Username",
|
|
32
|
+
description: "Agent username for platforms",
|
|
33
|
+
maxLength: 50
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
key: "bio",
|
|
37
|
+
type: "string | string[]",
|
|
38
|
+
label: "Bio",
|
|
39
|
+
description: "Biography — single string or array of points"
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
key: "system",
|
|
43
|
+
type: "string",
|
|
44
|
+
label: "System Prompt",
|
|
45
|
+
description: "System prompt defining core behavior",
|
|
46
|
+
maxLength: 1e4
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
key: "adjectives",
|
|
50
|
+
type: "string[]",
|
|
51
|
+
label: "Adjectives",
|
|
52
|
+
description: "Personality adjectives (e.g. curious, witty)"
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
key: "topics",
|
|
56
|
+
type: "string[]",
|
|
57
|
+
label: "Topics",
|
|
58
|
+
description: "Topics the agent is knowledgeable about"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
key: "style",
|
|
62
|
+
type: "object",
|
|
63
|
+
label: "Style",
|
|
64
|
+
description: "Communication style guides",
|
|
65
|
+
children: [
|
|
66
|
+
{
|
|
67
|
+
key: "all",
|
|
68
|
+
type: "string[]",
|
|
69
|
+
label: "All",
|
|
70
|
+
description: "Style guidelines for all responses"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
key: "chat",
|
|
74
|
+
type: "string[]",
|
|
75
|
+
label: "Chat",
|
|
76
|
+
description: "Style guidelines for chat responses"
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
key: "post",
|
|
80
|
+
type: "string[]",
|
|
81
|
+
label: "Post",
|
|
82
|
+
description: "Style guidelines for social media posts"
|
|
83
|
+
}
|
|
84
|
+
]
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
key: "messageExamples",
|
|
88
|
+
type: "array",
|
|
89
|
+
label: "Message Examples",
|
|
90
|
+
description: "Example conversations demonstrating the agent's voice"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
key: "postExamples",
|
|
94
|
+
type: "string[]",
|
|
95
|
+
label: "Post Examples",
|
|
96
|
+
description: "Example social media posts"
|
|
97
|
+
}
|
|
98
|
+
];
|
|
99
|
+
async function handleCharacterRoutes(ctx) {
|
|
100
|
+
const { req, res, method, pathname, state, readJsonBody, json, error, pickRandomNames } = ctx;
|
|
101
|
+
if (method === "GET" && pathname === "/api/character") {
|
|
102
|
+
const runtime = state.runtime;
|
|
103
|
+
const merged = {};
|
|
104
|
+
if (runtime) {
|
|
105
|
+
const character = runtime.character;
|
|
106
|
+
if (character.name) merged.name = character.name;
|
|
107
|
+
if (character.bio) merged.bio = character.bio;
|
|
108
|
+
if (character.system) merged.system = character.system;
|
|
109
|
+
if (character.adjectives) merged.adjectives = character.adjectives;
|
|
110
|
+
if (character.topics) merged.topics = character.topics;
|
|
111
|
+
if (character.style) merged.style = character.style;
|
|
112
|
+
if (character.postExamples) merged.postExamples = character.postExamples;
|
|
113
|
+
}
|
|
114
|
+
json(res, {
|
|
115
|
+
character: merged,
|
|
116
|
+
agentName: state.agentName
|
|
117
|
+
});
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
if (method === "PUT" && pathname === "/api/character") {
|
|
121
|
+
const body = await readJsonBody(req, res);
|
|
122
|
+
if (!body) return true;
|
|
123
|
+
const result = CharacterSchema.safeParse(body);
|
|
124
|
+
if (!result.success) {
|
|
125
|
+
json(res, {
|
|
126
|
+
ok: false,
|
|
127
|
+
validationErrors: result.error.issues.map((issue) => ({
|
|
128
|
+
path: issue.path.join("."),
|
|
129
|
+
message: issue.message
|
|
130
|
+
}))
|
|
131
|
+
}, 422);
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
if (state.runtime) {
|
|
135
|
+
const character = state.runtime.character;
|
|
136
|
+
if (body.name != null) character.name = String(body.name);
|
|
137
|
+
if (body.bio != null) character.bio = Array.isArray(body.bio) ? body.bio : [String(body.bio)];
|
|
138
|
+
if (body.system != null) character.system = String(body.system);
|
|
139
|
+
if (body.adjectives != null) character.adjectives = body.adjectives;
|
|
140
|
+
if (body.topics != null) character.topics = body.topics;
|
|
141
|
+
if (body.style != null) character.style = body.style;
|
|
142
|
+
if (body.postExamples != null) character.postExamples = body.postExamples;
|
|
143
|
+
}
|
|
144
|
+
if (body.name) state.agentName = String(body.name);
|
|
145
|
+
json(res, {
|
|
146
|
+
ok: true,
|
|
147
|
+
character: body,
|
|
148
|
+
agentName: state.agentName
|
|
149
|
+
});
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
if (method === "GET" && pathname === "/api/character/random-name") {
|
|
153
|
+
json(res, { name: pickRandomNames(1)[0] ?? "Reimu" });
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
if (method === "POST" && pathname === "/api/character/generate") {
|
|
157
|
+
const body = await readJsonBody(req, res);
|
|
158
|
+
if (!body) return true;
|
|
159
|
+
if (!body.field || !body.context) {
|
|
160
|
+
error(res, "field and context are required", 400);
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
const runtime = state.runtime;
|
|
164
|
+
if (!runtime) {
|
|
165
|
+
error(res, "Agent runtime not available. Start the agent first.", 503);
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
if (body.field !== "bio" && body.field !== "style" && body.field !== "chatExamples" && body.field !== "postExamples") {
|
|
169
|
+
error(res, `Unknown field: ${body.field}`, 400);
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
172
|
+
const prompt = buildGeneratePrompt(body.field, body.context, body.mode);
|
|
173
|
+
try {
|
|
174
|
+
const result = await runtime.useModel(ModelType.TEXT_SMALL, {
|
|
175
|
+
prompt,
|
|
176
|
+
temperature: .8,
|
|
177
|
+
maxTokens: 1500
|
|
178
|
+
});
|
|
179
|
+
json(res, { generated: String(result) });
|
|
180
|
+
} catch (err) {
|
|
181
|
+
const message = err instanceof Error ? err.message : "generation failed";
|
|
182
|
+
logger.error(`[character-generate] ${message}`);
|
|
183
|
+
error(res, message, 500);
|
|
184
|
+
}
|
|
185
|
+
return true;
|
|
186
|
+
}
|
|
187
|
+
if (method === "GET" && pathname === "/api/character/schema") {
|
|
188
|
+
json(res, { fields: CHARACTER_SCHEMA_FIELDS });
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
//#endregion
|
|
195
|
+
export { handleCharacterRoutes };
|