Compare commits
11 Commits
main
...
ui-build-d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d148c71368 | ||
|
|
bdb2a660d7 | ||
|
|
81ab281b51 | ||
|
|
81271f1b1c | ||
|
|
49a2db78c9 | ||
|
|
6640b43876 | ||
|
|
64f50e5c49 | ||
|
|
bd6e881eba | ||
|
|
22b224a343 | ||
|
|
ce9769ab09 | ||
|
|
71ecfb0b86 |
38
.github/workflows/ci.yml
vendored
38
.github/workflows/ci.yml
vendored
@ -91,10 +91,10 @@ jobs:
|
|||||||
run: ${{ matrix.command }}
|
run: ${{ matrix.command }}
|
||||||
|
|
||||||
checks-windows:
|
checks-windows:
|
||||||
runs-on: windows-latest
|
runs-on: windows-2022
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
shell: bash
|
shell: pwsh
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
@ -104,7 +104,7 @@ jobs:
|
|||||||
command: pnpm lint
|
command: pnpm lint
|
||||||
- runtime: node
|
- runtime: node
|
||||||
task: test
|
task: test
|
||||||
command: pnpm test
|
command: pnpm test -- --run --silent
|
||||||
- runtime: node
|
- runtime: node
|
||||||
task: build
|
task: build
|
||||||
command: pnpm build
|
command: pnpm build
|
||||||
@ -117,17 +117,22 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
submodules: false
|
submodules: false
|
||||||
|
|
||||||
|
- name: Force UTF-8 output (Windows)
|
||||||
|
run: |
|
||||||
|
chcp.com 65001
|
||||||
|
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||||
|
"LANG=en_US.UTF-8" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
|
||||||
|
"LC_ALL=en_US.UTF-8" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
|
||||||
|
|
||||||
- name: Checkout submodules (retry)
|
- name: Checkout submodules (retry)
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
|
||||||
git submodule sync --recursive
|
git submodule sync --recursive
|
||||||
for attempt in 1 2 3 4 5; do
|
for ($attempt = 1; $attempt -le 5; $attempt++) {
|
||||||
if git -c protocol.version=2 submodule update --init --force --depth=1 --recursive; then
|
git -c protocol.version=2 submodule update --init --force --depth=1 --recursive
|
||||||
exit 0
|
if ($LASTEXITCODE -eq 0) { exit 0 }
|
||||||
fi
|
Write-Host "Submodule update failed (attempt $attempt/5). Retrying..."
|
||||||
echo "Submodule update failed (attempt $attempt/5). Retrying…"
|
Start-Sleep -Seconds ($attempt * 10)
|
||||||
sleep $((attempt * 10))
|
}
|
||||||
done
|
|
||||||
exit 1
|
exit 1
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
@ -148,7 +153,9 @@ jobs:
|
|||||||
bun -v
|
bun -v
|
||||||
|
|
||||||
- name: Capture node path
|
- name: Capture node path
|
||||||
run: echo "NODE_BIN=$(dirname \"$(node -p \"process.execPath\")\")" >> "$GITHUB_ENV"
|
run: |
|
||||||
|
$nodeBin = Split-Path -Parent (node -p "process.execPath")
|
||||||
|
"NODE_BIN=$nodeBin" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
|
||||||
|
|
||||||
- name: Enable corepack and pin pnpm
|
- name: Enable corepack and pin pnpm
|
||||||
run: |
|
run: |
|
||||||
@ -160,14 +167,15 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
CI: true
|
CI: true
|
||||||
run: |
|
run: |
|
||||||
export PATH="$NODE_BIN:$PATH"
|
$env:PATH = "$env:NODE_BIN;$env:PATH"
|
||||||
which node
|
Get-Command node | Format-List
|
||||||
node -v
|
node -v
|
||||||
pnpm -v
|
pnpm -v
|
||||||
pnpm install --ignore-scripts=false --config.engine-strict=false --config.enable-pre-post-scripts=true || pnpm install --ignore-scripts=false --config.engine-strict=false --config.enable-pre-post-scripts=true
|
pnpm install --ignore-scripts=false --config.engine-strict=false --config.enable-pre-post-scripts=true || pnpm install --ignore-scripts=false --config.engine-strict=false --config.enable-pre-post-scripts=true
|
||||||
|
|
||||||
- name: Run ${{ matrix.task }} (${{ matrix.runtime }})
|
- name: Run ${{ matrix.task }} (${{ matrix.runtime }})
|
||||||
run: ${{ matrix.command }}
|
run: |
|
||||||
|
${{ matrix.command }}
|
||||||
|
|
||||||
macos-app:
|
macos-app:
|
||||||
if: github.event_name == 'pull_request'
|
if: github.event_name == 'pull_request'
|
||||||
|
|||||||
@ -46,6 +46,7 @@
|
|||||||
- Control UI: show/patch per-session reasoning level and render extracted reasoning in chat.
|
- Control UI: show/patch per-session reasoning level and render extracted reasoning in chat.
|
||||||
- Control UI: queue outgoing chat messages, add Enter-to-send, and show queued items. (#527) — thanks @YuriNachos
|
- Control UI: queue outgoing chat messages, add Enter-to-send, and show queued items. (#527) — thanks @YuriNachos
|
||||||
- Control UI: drop explicit `ui:install` step; `ui:build` now auto-installs UI deps (docs + update flow).
|
- Control UI: drop explicit `ui:install` step; `ui:build` now auto-installs UI deps (docs + update flow).
|
||||||
|
- Control UI: default control UI asset base to relative paths for subpath hosting. (#569) — thanks @bjesuiter
|
||||||
- Telegram: retry long-polling conflicts with backoff to avoid fatal exits.
|
- Telegram: retry long-polling conflicts with backoff to avoid fatal exits.
|
||||||
- Telegram: fix grammY fetch type mismatch when injecting `fetch`. (#512) — thanks @YuriNachos
|
- Telegram: fix grammY fetch type mismatch when injecting `fetch`. (#512) — thanks @YuriNachos
|
||||||
- WhatsApp: resolve @lid JIDs via Baileys mapping to unblock inbound messages. (#415)
|
- WhatsApp: resolve @lid JIDs via Baileys mapping to unblock inbound messages. (#415)
|
||||||
|
|||||||
@ -80,7 +80,7 @@ describe("Agent-specific sandbox config", () => {
|
|||||||
|
|
||||||
expect(context).toBeDefined();
|
expect(context).toBeDefined();
|
||||||
expect(context?.enabled).toBe(true);
|
expect(context?.enabled).toBe(true);
|
||||||
});
|
}, 20000);
|
||||||
|
|
||||||
it("should allow agent-specific docker setupCommand overrides", async () => {
|
it("should allow agent-specific docker setupCommand overrides", async () => {
|
||||||
const { resolveSandboxContext } = await import("./sandbox.js");
|
const { resolveSandboxContext } = await import("./sandbox.js");
|
||||||
|
|||||||
@ -179,14 +179,22 @@ async function ensureDevGatewayConfig(opts: { reset?: boolean }) {
|
|||||||
mode: "local",
|
mode: "local",
|
||||||
bind: "loopback",
|
bind: "loopback",
|
||||||
},
|
},
|
||||||
agent: {
|
agents: {
|
||||||
workspace,
|
defaults: {
|
||||||
skipBootstrap: true,
|
workspace,
|
||||||
},
|
skipBootstrap: true,
|
||||||
identity: {
|
},
|
||||||
name: DEV_IDENTITY_NAME,
|
list: [
|
||||||
theme: DEV_IDENTITY_THEME,
|
{
|
||||||
emoji: DEV_IDENTITY_EMOJI,
|
id: "main",
|
||||||
|
default: true,
|
||||||
|
identity: {
|
||||||
|
name: DEV_IDENTITY_NAME,
|
||||||
|
theme: DEV_IDENTITY_THEME,
|
||||||
|
emoji: DEV_IDENTITY_EMOJI,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
await ensureDevWorkspace(workspace);
|
await ensureDevWorkspace(workspace);
|
||||||
|
|||||||
@ -289,7 +289,7 @@ describe("doctor", () => {
|
|||||||
"+15555550123",
|
"+15555550123",
|
||||||
]);
|
]);
|
||||||
expect(written.routing).toBeUndefined();
|
expect(written.routing).toBeUndefined();
|
||||||
});
|
}, 20000);
|
||||||
|
|
||||||
it("migrates legacy Clawdis services", async () => {
|
it("migrates legacy Clawdis services", async () => {
|
||||||
readConfigFileSnapshot.mockResolvedValue({
|
readConfigFileSnapshot.mockResolvedValue({
|
||||||
|
|||||||
@ -10,12 +10,6 @@ const DEFAULT_MINIMAX_CONTEXT_WINDOW = 200000;
|
|||||||
const DEFAULT_MINIMAX_MAX_TOKENS = 8192;
|
const DEFAULT_MINIMAX_MAX_TOKENS = 8192;
|
||||||
export const MINIMAX_HOSTED_MODEL_REF = `minimax/${MINIMAX_HOSTED_MODEL_ID}`;
|
export const MINIMAX_HOSTED_MODEL_REF = `minimax/${MINIMAX_HOSTED_MODEL_ID}`;
|
||||||
|
|
||||||
const DEFAULT_MINIMAX_BASE_URL = "https://api.minimax.io/v1";
|
|
||||||
export const MINIMAX_HOSTED_MODEL_ID = "MiniMax-M2.1";
|
|
||||||
const DEFAULT_MINIMAX_CONTEXT_WINDOW = 200000;
|
|
||||||
const DEFAULT_MINIMAX_MAX_TOKENS = 8192;
|
|
||||||
export const MINIMAX_HOSTED_MODEL_REF = `minimax/${MINIMAX_HOSTED_MODEL_ID}`;
|
|
||||||
|
|
||||||
export async function writeOAuthCredentials(
|
export async function writeOAuthCredentials(
|
||||||
provider: OAuthProvider,
|
provider: OAuthProvider,
|
||||||
creds: OAuthCredentials,
|
creds: OAuthCredentials,
|
||||||
@ -176,7 +170,7 @@ export function applyMinimaxHostedProviderConfig(
|
|||||||
cfg: ClawdbotConfig,
|
cfg: ClawdbotConfig,
|
||||||
params?: { baseUrl?: string },
|
params?: { baseUrl?: string },
|
||||||
): ClawdbotConfig {
|
): ClawdbotConfig {
|
||||||
const models = { ...cfg.agent?.models };
|
const models = { ...cfg.agents?.defaults?.models };
|
||||||
models[MINIMAX_HOSTED_MODEL_REF] = {
|
models[MINIMAX_HOSTED_MODEL_REF] = {
|
||||||
...models[MINIMAX_HOSTED_MODEL_REF],
|
...models[MINIMAX_HOSTED_MODEL_REF],
|
||||||
alias: models[MINIMAX_HOSTED_MODEL_REF]?.alias ?? "Minimax",
|
alias: models[MINIMAX_HOSTED_MODEL_REF]?.alias ?? "Minimax",
|
||||||
@ -212,9 +206,12 @@ export function applyMinimaxHostedProviderConfig(
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
...cfg,
|
...cfg,
|
||||||
agent: {
|
agents: {
|
||||||
...cfg.agent,
|
...cfg.agents,
|
||||||
models,
|
defaults: {
|
||||||
|
...cfg.agents?.defaults,
|
||||||
|
models,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
models: {
|
models: {
|
||||||
mode: cfg.models?.mode ?? "merge",
|
mode: cfg.models?.mode ?? "merge",
|
||||||
@ -254,17 +251,21 @@ export function applyMinimaxHostedConfig(
|
|||||||
const next = applyMinimaxHostedProviderConfig(cfg, params);
|
const next = applyMinimaxHostedProviderConfig(cfg, params);
|
||||||
return {
|
return {
|
||||||
...next,
|
...next,
|
||||||
agent: {
|
agents: {
|
||||||
...next.agent,
|
...next.agents,
|
||||||
model: {
|
defaults: {
|
||||||
...(next.agent?.model &&
|
...next.agents?.defaults,
|
||||||
"fallbacks" in (next.agent.model as Record<string, unknown>)
|
model: {
|
||||||
? {
|
...(next.agents?.defaults?.model &&
|
||||||
fallbacks: (next.agent.model as { fallbacks?: string[] })
|
"fallbacks" in (next.agents.defaults.model as Record<string, unknown>)
|
||||||
.fallbacks,
|
? {
|
||||||
}
|
fallbacks: (
|
||||||
: undefined),
|
next.agents.defaults.model as { fallbacks?: string[] }
|
||||||
primary: MINIMAX_HOSTED_MODEL_REF,
|
).fallbacks,
|
||||||
|
}
|
||||||
|
: undefined),
|
||||||
|
primary: MINIMAX_HOSTED_MODEL_REF,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -546,7 +546,8 @@ async function promptWhatsAppAllowFrom(
|
|||||||
"WhatsApp number",
|
"WhatsApp number",
|
||||||
);
|
);
|
||||||
const entry = await prompter.text({
|
const entry = await prompter.text({
|
||||||
message: "Your personal WhatsApp number (the phone you will message from)",
|
message:
|
||||||
|
"Your personal WhatsApp number (the phone you will message from)",
|
||||||
placeholder: "+15555550123",
|
placeholder: "+15555550123",
|
||||||
initialValue: existingAllowFrom[0],
|
initialValue: existingAllowFrom[0],
|
||||||
validate: (value) => {
|
validate: (value) => {
|
||||||
@ -613,7 +614,8 @@ async function promptWhatsAppAllowFrom(
|
|||||||
"WhatsApp number",
|
"WhatsApp number",
|
||||||
);
|
);
|
||||||
const entry = await prompter.text({
|
const entry = await prompter.text({
|
||||||
message: "Your personal WhatsApp number (the phone you will message from)",
|
message:
|
||||||
|
"Your personal WhatsApp number (the phone you will message from)",
|
||||||
placeholder: "+15555550123",
|
placeholder: "+15555550123",
|
||||||
initialValue: existingAllowFrom[0],
|
initialValue: existingAllowFrom[0],
|
||||||
validate: (value) => {
|
validate: (value) => {
|
||||||
|
|||||||
@ -1202,7 +1202,7 @@ export type AgentDefaultsConfig = {
|
|||||||
every?: string;
|
every?: string;
|
||||||
/** Heartbeat model override (provider/model). */
|
/** Heartbeat model override (provider/model). */
|
||||||
model?: string;
|
model?: string;
|
||||||
/** Delivery target (last|whatsapp|telegram|discord|signal|imessage|none). */
|
/** Delivery target (last|whatsapp|telegram|discord|slack|signal|imessage|msteams|none). */
|
||||||
target?:
|
target?:
|
||||||
| "last"
|
| "last"
|
||||||
| "whatsapp"
|
| "whatsapp"
|
||||||
@ -1211,6 +1211,7 @@ export type AgentDefaultsConfig = {
|
|||||||
| "slack"
|
| "slack"
|
||||||
| "signal"
|
| "signal"
|
||||||
| "imessage"
|
| "imessage"
|
||||||
|
| "msteams"
|
||||||
| "none";
|
| "none";
|
||||||
/** Optional delivery override (E.164 for WhatsApp, chat id for Telegram). */
|
/** Optional delivery override (E.164 for WhatsApp, chat id for Telegram). */
|
||||||
to?: string;
|
to?: string;
|
||||||
|
|||||||
@ -603,6 +603,7 @@ const HeartbeatSchema = z
|
|||||||
z.literal("slack"),
|
z.literal("slack"),
|
||||||
z.literal("signal"),
|
z.literal("signal"),
|
||||||
z.literal("imessage"),
|
z.literal("imessage"),
|
||||||
|
z.literal("msteams"),
|
||||||
z.literal("none"),
|
z.literal("none"),
|
||||||
])
|
])
|
||||||
.optional(),
|
.optional(),
|
||||||
|
|||||||
@ -56,8 +56,9 @@ export async function monitorMSTeamsProvider(
|
|||||||
const textLimit = resolveTextChunkLimit(cfg, "msteams");
|
const textLimit = resolveTextChunkLimit(cfg, "msteams");
|
||||||
const MB = 1024 * 1024;
|
const MB = 1024 * 1024;
|
||||||
const mediaMaxBytes =
|
const mediaMaxBytes =
|
||||||
typeof cfg.agent?.mediaMaxMb === "number" && cfg.agent.mediaMaxMb > 0
|
typeof cfg.agents?.defaults?.mediaMaxMb === "number" &&
|
||||||
? Math.floor(cfg.agent.mediaMaxMb * MB)
|
cfg.agents.defaults.mediaMaxMb > 0
|
||||||
|
? Math.floor(cfg.agents.defaults.mediaMaxMb * MB)
|
||||||
: 8 * MB;
|
: 8 * MB;
|
||||||
const conversationStore =
|
const conversationStore =
|
||||||
opts.conversationStore ?? createMSTeamsConversationStoreFs();
|
opts.conversationStore ?? createMSTeamsConversationStoreFs();
|
||||||
|
|||||||
@ -122,9 +122,7 @@ export async function sendMessageTelegram(
|
|||||||
const client: ApiClientOptions | undefined = fetchImpl
|
const client: ApiClientOptions | undefined = fetchImpl
|
||||||
? { fetch: fetchImpl as unknown as ApiClientOptions["fetch"] }
|
? { fetch: fetchImpl as unknown as ApiClientOptions["fetch"] }
|
||||||
: undefined;
|
: undefined;
|
||||||
const api =
|
const api = opts.api ?? new Bot(token, client ? { client } : undefined).api;
|
||||||
opts.api ??
|
|
||||||
new Bot(token, client ? { client } : undefined).api;
|
|
||||||
const mediaUrl = opts.mediaUrl?.trim();
|
const mediaUrl = opts.mediaUrl?.trim();
|
||||||
|
|
||||||
// Build optional params for forum topics and reply threading.
|
// Build optional params for forum topics and reply threading.
|
||||||
@ -296,9 +294,7 @@ export async function reactMessageTelegram(
|
|||||||
const client: ApiClientOptions | undefined = fetchImpl
|
const client: ApiClientOptions | undefined = fetchImpl
|
||||||
? { fetch: fetchImpl as unknown as ApiClientOptions["fetch"] }
|
? { fetch: fetchImpl as unknown as ApiClientOptions["fetch"] }
|
||||||
: undefined;
|
: undefined;
|
||||||
const api =
|
const api = opts.api ?? new Bot(token, client ? { client } : undefined).api;
|
||||||
opts.api ??
|
|
||||||
new Bot(token, client ? { client } : undefined).api;
|
|
||||||
const request = createTelegramRetryRunner({
|
const request = createTelegramRetryRunner({
|
||||||
retry: opts.retry,
|
retry: opts.retry,
|
||||||
configRetry: account.config.retry,
|
configRetry: account.config.retry,
|
||||||
|
|||||||
@ -11,10 +11,7 @@ export async function setTelegramWebhook(opts: {
|
|||||||
const client: ApiClientOptions | undefined = fetchImpl
|
const client: ApiClientOptions | undefined = fetchImpl
|
||||||
? { fetch: fetchImpl as unknown as ApiClientOptions["fetch"] }
|
? { fetch: fetchImpl as unknown as ApiClientOptions["fetch"] }
|
||||||
: undefined;
|
: undefined;
|
||||||
const bot = new Bot(
|
const bot = new Bot(opts.token, client ? { client } : undefined);
|
||||||
opts.token,
|
|
||||||
client ? { client } : undefined,
|
|
||||||
);
|
|
||||||
await bot.api.setWebhook(opts.url, {
|
await bot.api.setWebhook(opts.url, {
|
||||||
secret_token: opts.secret,
|
secret_token: opts.secret,
|
||||||
drop_pending_updates: opts.dropPendingUpdates ?? false,
|
drop_pending_updates: opts.dropPendingUpdates ?? false,
|
||||||
@ -26,9 +23,6 @@ export async function deleteTelegramWebhook(opts: { token: string }) {
|
|||||||
const client: ApiClientOptions | undefined = fetchImpl
|
const client: ApiClientOptions | undefined = fetchImpl
|
||||||
? { fetch: fetchImpl as unknown as ApiClientOptions["fetch"] }
|
? { fetch: fetchImpl as unknown as ApiClientOptions["fetch"] }
|
||||||
: undefined;
|
: undefined;
|
||||||
const bot = new Bot(
|
const bot = new Bot(opts.token, client ? { client } : undefined);
|
||||||
opts.token,
|
|
||||||
client ? { client } : undefined,
|
|
||||||
);
|
|
||||||
await bot.api.deleteWebhook();
|
await bot.api.deleteWebhook();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,48 @@ import fs from "node:fs";
|
|||||||
import os from "node:os";
|
import os from "node:os";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
|
|
||||||
|
const shouldSanitizeConsoleOutput =
|
||||||
|
process.platform === "win32" && process.env.GITHUB_ACTIONS === "true";
|
||||||
|
|
||||||
|
if (shouldSanitizeConsoleOutput) {
|
||||||
|
const sanitize = (value: string) => {
|
||||||
|
let out = "";
|
||||||
|
for (const ch of value) {
|
||||||
|
const code = ch.charCodeAt(0);
|
||||||
|
if (code === 9 || code === 10 || code === 13) {
|
||||||
|
out += ch;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (code >= 32 && code <= 126) {
|
||||||
|
out += ch;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
out += "?";
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
};
|
||||||
|
|
||||||
|
const patchStream = (stream: NodeJS.WriteStream) => {
|
||||||
|
const originalWrite = stream.write.bind(stream);
|
||||||
|
stream.write = ((chunk: unknown, encoding?: unknown, cb?: unknown) => {
|
||||||
|
if (typeof chunk === "string") {
|
||||||
|
return originalWrite(sanitize(chunk), encoding as never, cb as never);
|
||||||
|
}
|
||||||
|
if (Buffer.isBuffer(chunk)) {
|
||||||
|
return originalWrite(
|
||||||
|
sanitize(chunk.toString("utf8")),
|
||||||
|
encoding as never,
|
||||||
|
cb as never,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return originalWrite(chunk as never, encoding as never, cb as never);
|
||||||
|
}) as typeof stream.write;
|
||||||
|
};
|
||||||
|
|
||||||
|
patchStream(process.stdout);
|
||||||
|
patchStream(process.stderr);
|
||||||
|
}
|
||||||
|
|
||||||
const originalHome = process.env.HOME;
|
const originalHome = process.env.HOME;
|
||||||
const originalUserProfile = process.env.USERPROFILE;
|
const originalUserProfile = process.env.USERPROFILE;
|
||||||
const originalXdgConfigHome = process.env.XDG_CONFIG_HOME;
|
const originalXdgConfigHome = process.env.XDG_CONFIG_HOME;
|
||||||
|
|||||||
@ -14,7 +14,7 @@ function normalizeBase(input: string): string {
|
|||||||
|
|
||||||
export default defineConfig(({ command }) => {
|
export default defineConfig(({ command }) => {
|
||||||
const envBase = process.env.CLAWDBOT_CONTROL_UI_BASE_PATH?.trim();
|
const envBase = process.env.CLAWDBOT_CONTROL_UI_BASE_PATH?.trim();
|
||||||
const base = envBase ? normalizeBase(envBase) : "/";
|
const base = envBase ? normalizeBase(envBase) : "./";
|
||||||
return {
|
return {
|
||||||
base,
|
base,
|
||||||
publicDir: path.resolve(here, "public"),
|
publicDir: path.resolve(here, "public"),
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user