@geminilight/mindos 0.6.28 → 0.6.29
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/app/app/api/a2a/agents/route.ts +9 -0
- package/app/app/api/a2a/delegations/route.ts +9 -0
- package/app/app/api/a2a/discover/route.ts +2 -0
- package/app/app/api/a2a/route.ts +6 -6
- package/app/app/api/acp/detect/route.ts +91 -0
- package/app/app/api/acp/registry/route.ts +31 -0
- package/app/app/api/acp/session/route.ts +55 -0
- package/app/app/layout.tsx +2 -0
- package/app/components/DirView.tsx +64 -2
- package/app/components/FileTree.tsx +19 -0
- package/app/components/GuideCard.tsx +7 -17
- package/app/components/MarkdownView.tsx +2 -0
- package/app/components/SearchModal.tsx +234 -80
- package/app/components/agents/AgentDetailContent.tsx +3 -5
- package/app/components/agents/AgentsContentPage.tsx +21 -6
- package/app/components/agents/AgentsPanelA2aTab.tsx +445 -0
- package/app/components/agents/SkillDetailPopover.tsx +4 -9
- package/app/components/agents/agents-content-model.ts +2 -2
- package/app/components/help/HelpContent.tsx +9 -9
- package/app/components/panels/AgentsPanel.tsx +1 -0
- package/app/components/panels/AgentsPanelAgentDetail.tsx +5 -8
- package/app/components/panels/AgentsPanelHubNav.tsx +8 -1
- package/app/components/panels/EchoPanel.tsx +5 -1
- package/app/components/panels/EchoSidebarStats.tsx +136 -0
- package/app/components/settings/KnowledgeTab.tsx +3 -6
- package/app/components/settings/McpSkillsSection.tsx +4 -5
- package/app/components/settings/McpTab.tsx +6 -8
- package/app/components/setup/StepSecurity.tsx +4 -5
- package/app/components/setup/index.tsx +5 -11
- package/app/components/ui/Toaster.tsx +39 -0
- package/app/hooks/useA2aRegistry.ts +6 -1
- package/app/hooks/useAcpDetection.ts +65 -0
- package/app/hooks/useAcpRegistry.ts +51 -0
- package/app/hooks/useDelegationHistory.ts +49 -0
- package/app/lib/a2a/client.ts +49 -5
- package/app/lib/a2a/orchestrator.ts +0 -1
- package/app/lib/a2a/task-handler.ts +4 -4
- package/app/lib/a2a/types.ts +15 -0
- package/app/lib/acp/acp-tools.ts +93 -0
- package/app/lib/acp/bridge.ts +138 -0
- package/app/lib/acp/index.ts +24 -0
- package/app/lib/acp/registry.ts +135 -0
- package/app/lib/acp/session.ts +264 -0
- package/app/lib/acp/subprocess.ts +209 -0
- package/app/lib/acp/types.ts +136 -0
- package/app/lib/agent/tools.ts +2 -1
- package/app/lib/i18n/_core.ts +22 -0
- package/app/lib/i18n/index.ts +35 -0
- package/app/lib/i18n/modules/ai-chat.ts +215 -0
- package/app/lib/i18n/modules/common.ts +71 -0
- package/app/lib/i18n/modules/features.ts +153 -0
- package/app/lib/i18n/modules/knowledge.ts +425 -0
- package/app/lib/i18n/modules/navigation.ts +151 -0
- package/app/lib/i18n/modules/onboarding.ts +523 -0
- package/app/lib/i18n/modules/panels.ts +1052 -0
- package/app/lib/i18n/modules/settings.ts +585 -0
- package/app/lib/i18n-en.ts +2 -1518
- package/app/lib/i18n-zh.ts +2 -1542
- package/app/lib/i18n.ts +3 -6
- package/app/lib/toast.ts +79 -0
- package/bin/cli.js +25 -25
- package/bin/commands/file.js +29 -2
- package/bin/commands/space.js +249 -91
- package/package.json +1 -1
package/app/lib/i18n.ts
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export type Locale = 'en' | 'zh';
|
|
5
|
-
export const messages = { en, zh } as const;
|
|
6
|
-
export type Messages = typeof messages['en'];
|
|
1
|
+
// Re-exports from modular i18n system.
|
|
2
|
+
// All translations are now organized in ./i18n/modules/*.ts
|
|
3
|
+
export { en, zh, messages, type Locale, type Messages } from './i18n/index';
|
package/app/lib/toast.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global toast notification store.
|
|
3
|
+
* Uses module-level state + useSyncExternalStore (no Context/Provider needed).
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* import { toast } from '@/lib/toast';
|
|
7
|
+
* toast.success('Saved!');
|
|
8
|
+
* toast.error('Something went wrong');
|
|
9
|
+
* toast.copy(); // "Copied" with check icon
|
|
10
|
+
* toast('Custom message'); // info type
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export interface Toast {
|
|
14
|
+
id: string;
|
|
15
|
+
message: string;
|
|
16
|
+
type: 'success' | 'error' | 'info';
|
|
17
|
+
duration: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
type ToastInput = { message: string; type?: Toast['type']; duration?: number };
|
|
21
|
+
|
|
22
|
+
const DEFAULT_DURATION = 2000;
|
|
23
|
+
const MAX_TOASTS = 3;
|
|
24
|
+
|
|
25
|
+
let toasts: Toast[] = [];
|
|
26
|
+
let listeners: Array<() => void> = [];
|
|
27
|
+
let nextId = 0;
|
|
28
|
+
|
|
29
|
+
function emit() {
|
|
30
|
+
for (const fn of listeners) fn();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function subscribe(listener: () => void) {
|
|
34
|
+
listeners = [...listeners, listener];
|
|
35
|
+
return () => { listeners = listeners.filter((l) => l !== listener); };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function getSnapshot(): Toast[] {
|
|
39
|
+
return toasts;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function dismiss(id: string) {
|
|
43
|
+
toasts = toasts.filter((t) => t.id !== id);
|
|
44
|
+
emit();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function addToast(input: ToastInput) {
|
|
48
|
+
const id = `toast-${++nextId}`;
|
|
49
|
+
const t: Toast = {
|
|
50
|
+
id,
|
|
51
|
+
message: input.message,
|
|
52
|
+
type: input.type ?? 'info',
|
|
53
|
+
duration: input.duration ?? DEFAULT_DURATION,
|
|
54
|
+
};
|
|
55
|
+
toasts = [...toasts, t].slice(-MAX_TOASTS);
|
|
56
|
+
emit();
|
|
57
|
+
if (t.duration > 0) {
|
|
58
|
+
setTimeout(() => dismiss(id), t.duration);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Show an info toast */
|
|
63
|
+
function toast(message: string, opts?: { type?: Toast['type']; duration?: number }) {
|
|
64
|
+
addToast({ message, ...opts });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Show a success toast */
|
|
68
|
+
toast.success = (message: string, duration?: number) =>
|
|
69
|
+
addToast({ message, type: 'success', duration });
|
|
70
|
+
|
|
71
|
+
/** Show an error toast */
|
|
72
|
+
toast.error = (message: string, duration?: number) =>
|
|
73
|
+
addToast({ message, type: 'error', duration });
|
|
74
|
+
|
|
75
|
+
/** Show a "Copied" success toast */
|
|
76
|
+
toast.copy = (message = 'Copied') =>
|
|
77
|
+
addToast({ message, type: 'success', duration: 1500 });
|
|
78
|
+
|
|
79
|
+
export { toast };
|
package/bin/cli.js
CHANGED
|
@@ -56,7 +56,7 @@ import { savePids, clearPids } from './lib/pid.js';
|
|
|
56
56
|
import { stopMindos } from './lib/stop.js';
|
|
57
57
|
import { printStartupInfo, getLocalIP } from './lib/startup.js';
|
|
58
58
|
import { spawnMcp } from './lib/mcp-spawn.js';
|
|
59
|
-
import { parseArgs } from './lib/command.js';
|
|
59
|
+
import { parseArgs, EXIT } from './lib/command.js';
|
|
60
60
|
import { MCP_AGENTS, detectAgentPresence } from './lib/mcp-agents.js';
|
|
61
61
|
|
|
62
62
|
// Heavy modules — loaded lazily inside command handlers to speed up CLI cold start
|
|
@@ -215,7 +215,7 @@ const commands = {
|
|
|
215
215
|
token: () => {
|
|
216
216
|
if (!existsSync(CONFIG_PATH)) {
|
|
217
217
|
console.error(red('No config found. Run `mindos onboard` first.'));
|
|
218
|
-
process.exit(
|
|
218
|
+
process.exit(EXIT.ERROR);
|
|
219
219
|
}
|
|
220
220
|
let config = {};
|
|
221
221
|
try { config = JSON.parse(readFileSync(CONFIG_PATH, 'utf-8')); } catch {}
|
|
@@ -325,7 +325,7 @@ const commands = {
|
|
|
325
325
|
if (!ready) {
|
|
326
326
|
console.error(red('\n✘ Service started but Web UI did not become ready in time.'));
|
|
327
327
|
console.error(dim(' Check logs with: mindos logs\n'));
|
|
328
|
-
process.exit(
|
|
328
|
+
process.exit(EXIT.ERROR);
|
|
329
329
|
}
|
|
330
330
|
await printStartupInfo(webPort, mcpPort);
|
|
331
331
|
// System notification
|
|
@@ -381,7 +381,7 @@ const commands = {
|
|
|
381
381
|
const mcpOk = await waitForPortFree(Number(mcpPort), { retries: 60, intervalMs: 500 });
|
|
382
382
|
if (!webOk || !mcpOk) {
|
|
383
383
|
console.error('Ports still in use after 30s, exiting.');
|
|
384
|
-
process.exit(
|
|
384
|
+
process.exit(EXIT.ERROR); // KeepAlive will retry after ThrottleInterval
|
|
385
385
|
}
|
|
386
386
|
} else {
|
|
387
387
|
await assertPortFree(Number(webPort), 'web');
|
|
@@ -515,19 +515,19 @@ ${dim('Shortcut: mindos start --daemon → install + start in one step')}
|
|
|
515
515
|
|
|
516
516
|
if (!existsSync(CONFIG_PATH)) {
|
|
517
517
|
console.log(` ${red('✘')} Config not found. Run ${cyan('mindos onboard')} first.\n`);
|
|
518
|
-
process.exit(
|
|
518
|
+
process.exit(EXIT.ERROR);
|
|
519
519
|
}
|
|
520
520
|
let config;
|
|
521
521
|
try {
|
|
522
522
|
config = JSON.parse(readFileSync(CONFIG_PATH, 'utf-8'));
|
|
523
523
|
} catch {
|
|
524
524
|
console.log(` ${red('✘')} Failed to parse config at ${dim(CONFIG_PATH)}\n`);
|
|
525
|
-
process.exit(
|
|
525
|
+
process.exit(EXIT.ERROR);
|
|
526
526
|
}
|
|
527
527
|
const mindRoot = config.mindRoot;
|
|
528
528
|
if (!mindRoot || !existsSync(mindRoot)) {
|
|
529
529
|
console.log(` ${red('✘')} Knowledge base not found: ${dim(mindRoot || '(not set)')}\n`);
|
|
530
|
-
process.exit(
|
|
530
|
+
process.exit(EXIT.ERROR);
|
|
531
531
|
}
|
|
532
532
|
|
|
533
533
|
// Skill operating rules are now built into SKILL.md (shipped with the app).
|
|
@@ -565,7 +565,7 @@ ${dim('Shortcut: mindos start --daemon → install + start in one step')}
|
|
|
565
565
|
err(`Config not found at ${dim(CONFIG_PATH)}`, 'config');
|
|
566
566
|
if (!jsonMode) { console.log(`\n ${dim('Run `mindos onboard` to create it.')}\n`); }
|
|
567
567
|
if (jsonMode) { console.log(JSON.stringify({ ok: false, checks }, null, 2)); }
|
|
568
|
-
process.exit(
|
|
568
|
+
process.exit(EXIT.ERROR);
|
|
569
569
|
}
|
|
570
570
|
let config;
|
|
571
571
|
try {
|
|
@@ -719,7 +719,7 @@ ${dim('Shortcut: mindos start --daemon → install + start in one step')}
|
|
|
719
719
|
? `\n${red('Some checks failed.')} Run ${cyan('mindos onboard')} to reconfigure.\n`
|
|
720
720
|
: `\n${green('All checks passed.')}\n`);
|
|
721
721
|
}
|
|
722
|
-
if (hasError) process.exit(
|
|
722
|
+
if (hasError) process.exit(EXIT.ERROR);
|
|
723
723
|
},
|
|
724
724
|
|
|
725
725
|
// ── update ─────────────────────────────────────────────────────────────────
|
|
@@ -737,7 +737,7 @@ ${dim('Shortcut: mindos start --daemon → install + start in one step')}
|
|
|
737
737
|
} catch {
|
|
738
738
|
writeUpdateFailed('downloading', 'npm install failed', { fromVersion: currentVersion });
|
|
739
739
|
console.error(red('Update failed. Try: npm install -g @geminilight/mindos@latest'));
|
|
740
|
-
process.exit(
|
|
740
|
+
process.exit(EXIT.ERROR);
|
|
741
741
|
}
|
|
742
742
|
if (existsSync(BUILD_STAMP)) rmSync(BUILD_STAMP);
|
|
743
743
|
|
|
@@ -820,7 +820,7 @@ ${dim('Shortcut: mindos start --daemon → install + start in one step')}
|
|
|
820
820
|
: 'Server did not come back up in time';
|
|
821
821
|
writeUpdateFailed('restarting', failMsg, vOpts);
|
|
822
822
|
console.error(red(`✘ ${failMsg}. Check logs: mindos logs\n`));
|
|
823
|
-
process.exit(
|
|
823
|
+
process.exit(EXIT.ERROR);
|
|
824
824
|
}
|
|
825
825
|
} else {
|
|
826
826
|
// Non-daemon mode: check if a MindOS instance is currently running
|
|
@@ -890,7 +890,7 @@ ${dim('Shortcut: mindos start --daemon → install + start in one step')}
|
|
|
890
890
|
: 'Server did not come back up in time';
|
|
891
891
|
writeUpdateFailed('restarting', failMsg, vOpts);
|
|
892
892
|
console.error(red(`✘ ${failMsg}. Check logs: mindos logs\n`));
|
|
893
|
-
process.exit(
|
|
893
|
+
process.exit(EXIT.ERROR);
|
|
894
894
|
}
|
|
895
895
|
} else {
|
|
896
896
|
// No running instance — just build and tell user to start manually
|
|
@@ -1071,12 +1071,12 @@ ${dim('Shortcut: mindos start --daemon → install + start in one step')}
|
|
|
1071
1071
|
if (sub === 'show') {
|
|
1072
1072
|
if (!existsSync(CONFIG_PATH)) {
|
|
1073
1073
|
console.error(red('No config found. Run `mindos onboard` first.'));
|
|
1074
|
-
process.exit(
|
|
1074
|
+
process.exit(EXIT.ERROR);
|
|
1075
1075
|
}
|
|
1076
1076
|
let config;
|
|
1077
1077
|
try { config = JSON.parse(readFileSync(CONFIG_PATH, 'utf-8')); } catch {
|
|
1078
1078
|
console.error(red('Failed to parse config file.'));
|
|
1079
|
-
process.exit(
|
|
1079
|
+
process.exit(EXIT.ERROR);
|
|
1080
1080
|
}
|
|
1081
1081
|
const display = JSON.parse(JSON.stringify(config));
|
|
1082
1082
|
if (display.ai?.providers?.anthropic?.apiKey)
|
|
@@ -1104,14 +1104,14 @@ ${dim('Shortcut: mindos start --daemon → install + start in one step')}
|
|
|
1104
1104
|
if (sub === 'validate') {
|
|
1105
1105
|
if (!existsSync(CONFIG_PATH)) {
|
|
1106
1106
|
console.error(red('No config found. Run `mindos onboard` first.'));
|
|
1107
|
-
process.exit(
|
|
1107
|
+
process.exit(EXIT.ERROR);
|
|
1108
1108
|
}
|
|
1109
1109
|
let config;
|
|
1110
1110
|
try {
|
|
1111
1111
|
config = JSON.parse(readFileSync(CONFIG_PATH, 'utf-8'));
|
|
1112
1112
|
} catch (e) {
|
|
1113
1113
|
console.error(red(`✘ Invalid JSON: ${e.message}`));
|
|
1114
|
-
process.exit(
|
|
1114
|
+
process.exit(EXIT.ERROR);
|
|
1115
1115
|
}
|
|
1116
1116
|
const issues = [];
|
|
1117
1117
|
if (!config.mindRoot) issues.push('missing required field: mindRoot');
|
|
@@ -1128,7 +1128,7 @@ ${dim('Shortcut: mindos start --daemon → install + start in one step')}
|
|
|
1128
1128
|
console.error(`\n${red('✘ Config has issues:')}`);
|
|
1129
1129
|
issues.forEach(i => console.error(` ${red('•')} ${i}`));
|
|
1130
1130
|
console.error(`\n ${dim('Run `mindos onboard` to fix.\n')}`);
|
|
1131
|
-
process.exit(
|
|
1131
|
+
process.exit(EXIT.ERROR);
|
|
1132
1132
|
}
|
|
1133
1133
|
console.log(`\n${green('✔ Config is valid')}\n`);
|
|
1134
1134
|
return;
|
|
@@ -1143,16 +1143,16 @@ ${dim('Shortcut: mindos start --daemon → install + start in one step')}
|
|
|
1143
1143
|
console.error(dim(' mindos config set port 3002'));
|
|
1144
1144
|
console.error(dim(' mindos config set mcpPort 8788'));
|
|
1145
1145
|
console.error(dim(' mindos config set ai.provider openai'));
|
|
1146
|
-
process.exit(
|
|
1146
|
+
process.exit(EXIT.ARGS);
|
|
1147
1147
|
}
|
|
1148
1148
|
if (!existsSync(CONFIG_PATH)) {
|
|
1149
1149
|
console.error(red('No config found. Run `mindos onboard` first.'));
|
|
1150
|
-
process.exit(
|
|
1150
|
+
process.exit(EXIT.ERROR);
|
|
1151
1151
|
}
|
|
1152
1152
|
let config;
|
|
1153
1153
|
try { config = JSON.parse(readFileSync(CONFIG_PATH, 'utf-8')); } catch {
|
|
1154
1154
|
console.error(red('Failed to parse config file.'));
|
|
1155
|
-
process.exit(
|
|
1155
|
+
process.exit(EXIT.ERROR);
|
|
1156
1156
|
}
|
|
1157
1157
|
const parts = key.split('.');
|
|
1158
1158
|
let obj = config;
|
|
@@ -1180,16 +1180,16 @@ ${dim('Shortcut: mindos start --daemon → install + start in one step')}
|
|
|
1180
1180
|
const key = cliArgs[1];
|
|
1181
1181
|
if (!key) {
|
|
1182
1182
|
console.error(red('Usage: mindos config unset <key>'));
|
|
1183
|
-
process.exit(
|
|
1183
|
+
process.exit(EXIT.ARGS);
|
|
1184
1184
|
}
|
|
1185
1185
|
if (!existsSync(CONFIG_PATH)) {
|
|
1186
1186
|
console.error(red('No config found. Run `mindos onboard` first.'));
|
|
1187
|
-
process.exit(
|
|
1187
|
+
process.exit(EXIT.ERROR);
|
|
1188
1188
|
}
|
|
1189
1189
|
let config;
|
|
1190
1190
|
try { config = JSON.parse(readFileSync(CONFIG_PATH, 'utf-8')); } catch {
|
|
1191
1191
|
console.error(red('Failed to parse config file.'));
|
|
1192
|
-
process.exit(
|
|
1192
|
+
process.exit(EXIT.ERROR);
|
|
1193
1193
|
}
|
|
1194
1194
|
const parts = key.split('.');
|
|
1195
1195
|
let obj = config;
|
|
@@ -1256,7 +1256,7 @@ ${bold('Examples:')}
|
|
|
1256
1256
|
console.log(green('✔ Sync complete'));
|
|
1257
1257
|
} catch (err) {
|
|
1258
1258
|
console.error(red(err.message));
|
|
1259
|
-
process.exit(
|
|
1259
|
+
process.exit(EXIT.ERROR);
|
|
1260
1260
|
}
|
|
1261
1261
|
return;
|
|
1262
1262
|
}
|
|
@@ -1283,7 +1283,7 @@ ${bold('Examples:')}
|
|
|
1283
1283
|
if (!validSubs.includes(sub)) {
|
|
1284
1284
|
console.error(red(`Unknown sync subcommand: ${sub}`));
|
|
1285
1285
|
console.error(dim(`Available: ${validSubs.join(' | ')}`));
|
|
1286
|
-
process.exit(
|
|
1286
|
+
process.exit(EXIT.ARGS);
|
|
1287
1287
|
}
|
|
1288
1288
|
}
|
|
1289
1289
|
|
package/bin/commands/file.js
CHANGED
|
@@ -30,7 +30,7 @@ function resolvePath(root, filePath) {
|
|
|
30
30
|
export const meta = {
|
|
31
31
|
name: 'file',
|
|
32
32
|
group: 'Knowledge',
|
|
33
|
-
summary: '
|
|
33
|
+
summary: 'File content operations (list, read, create, delete, rename, search)',
|
|
34
34
|
usage: 'mindos file <subcommand>',
|
|
35
35
|
flags: {
|
|
36
36
|
'--space <name>': 'Filter by space name',
|
|
@@ -68,9 +68,12 @@ export async function run(args, flags) {
|
|
|
68
68
|
case 'mv': return fileRename(root, args[1], args[2], flags);
|
|
69
69
|
case 'move': return fileRename(root, args[1], args[2], flags);
|
|
70
70
|
case 'search': return fileSearch(root, args.slice(1).join(' '), flags);
|
|
71
|
+
case 'mkdir':
|
|
72
|
+
console.log(dim('Moved to: mindos space mkdir <path>'));
|
|
73
|
+
process.exit(EXIT.ARGS);
|
|
71
74
|
default:
|
|
72
75
|
console.error(red(`Unknown subcommand: ${sub}`));
|
|
73
|
-
console.error(dim('Available: list, read, create, delete, rename, move, search'));
|
|
76
|
+
console.error(dim('Available: list, read, create, delete, rename, move, mkdir, search'));
|
|
74
77
|
process.exit(EXIT.ERROR);
|
|
75
78
|
}
|
|
76
79
|
}
|
|
@@ -284,3 +287,27 @@ function fileSearch(root, query, flags) {
|
|
|
284
287
|
}
|
|
285
288
|
console.log();
|
|
286
289
|
}
|
|
290
|
+
|
|
291
|
+
function fileMkdir(root, dirPath, flags) {
|
|
292
|
+
if (!dirPath) {
|
|
293
|
+
console.error(red('Usage: mindos file mkdir <path>'));
|
|
294
|
+
process.exit(EXIT.ERROR);
|
|
295
|
+
}
|
|
296
|
+
const full = resolve(root, dirPath);
|
|
297
|
+
if (existsSync(full)) {
|
|
298
|
+
if (isJsonMode(flags)) {
|
|
299
|
+
output({ ok: true, path: dirPath, created: false, message: 'already exists' }, flags);
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
console.log(dim(`Directory already exists: ${dirPath}`));
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
mkdirSync(full, { recursive: true });
|
|
307
|
+
|
|
308
|
+
if (isJsonMode(flags)) {
|
|
309
|
+
output({ ok: true, path: dirPath, created: true }, flags);
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
console.log(`${green('✔')} Created directory: ${cyan(dirPath)}`);
|
|
313
|
+
}
|