@cccarv82/freya 1.0.7 → 1.0.9

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.
Files changed (2) hide show
  1. package/cli/web.js +51 -12
  2. package/package.json +1 -1
package/cli/web.js CHANGED
@@ -6,11 +6,18 @@ const path = require('path');
6
6
  const { spawn } = require('child_process');
7
7
 
8
8
  function guessNpmCmd() {
9
- return process.platform === 'win32' ? 'npm.cmd' : 'npm';
9
+ // We'll execute via cmd.exe on Windows for reliability.
10
+ return process.platform === 'win32' ? 'npm' : 'npm';
10
11
  }
11
12
 
12
13
  function guessNpxCmd() {
13
- return process.platform === 'win32' ? 'npx.cmd' : 'npx';
14
+ // We'll execute via cmd.exe on Windows for reliability.
15
+ return process.platform === 'win32' ? 'npx' : 'npx';
16
+ }
17
+
18
+ function guessNpxYesFlag() {
19
+ // npx supports --yes/-y on modern npm; use -y for broad compatibility
20
+ return '-y';
14
21
  }
15
22
 
16
23
  function guessOpenCmd() {
@@ -62,6 +69,26 @@ function safeJson(res, code, obj) {
62
69
  res.end(body);
63
70
  }
64
71
 
72
+ function looksLikeFreyaWorkspace(dir) {
73
+ // minimal check: has scripts/validate-data.js and data/
74
+ return (
75
+ exists(path.join(dir, 'package.json')) &&
76
+ exists(path.join(dir, 'scripts')) &&
77
+ exists(path.join(dir, 'data'))
78
+ );
79
+ }
80
+
81
+ function normalizeWorkspaceDir(inputDir) {
82
+ const d = path.resolve(process.cwd(), inputDir);
83
+ if (looksLikeFreyaWorkspace(d)) return d;
84
+
85
+ // Common case: user picked parent folder that contains ./freya
86
+ const child = path.join(d, 'freya');
87
+ if (looksLikeFreyaWorkspace(child)) return child;
88
+
89
+ return d;
90
+ }
91
+
65
92
  function readBody(req) {
66
93
  return new Promise((resolve, reject) => {
67
94
  const chunks = [];
@@ -74,8 +101,15 @@ function readBody(req) {
74
101
  function run(cmd, args, cwd) {
75
102
  return new Promise((resolve) => {
76
103
  let child;
104
+
77
105
  try {
78
- child = spawn(cmd, args, { cwd, shell: false, env: process.env });
106
+ // On Windows, reliably execute CLI tools through cmd.exe.
107
+ if (process.platform === 'win32' && (cmd === 'npx' || cmd === 'npm')) {
108
+ const comspec = process.env.ComSpec || 'cmd.exe';
109
+ child = spawn(comspec, ['/d', '/s', '/c', cmd, ...args], { cwd, shell: false, env: process.env });
110
+ } else {
111
+ child = spawn(cmd, args, { cwd, shell: false, env: process.env });
112
+ }
79
113
  } catch (e) {
80
114
  return resolve({ code: 1, stdout: '', stderr: e.message || String(e) });
81
115
  }
@@ -90,7 +124,7 @@ function run(cmd, args, cwd) {
90
124
  stderr += d.toString();
91
125
  });
92
126
 
93
- // Prevent unhandled error event (e.g., ENOENT on Windows when cmd not found)
127
+ // Prevent unhandled error event (e.g., ENOENT/EINVAL)
94
128
  child.on('error', (e) => {
95
129
  stderr += `\n${e.message || String(e)}`;
96
130
  resolve({ code: 1, stdout, stderr });
@@ -1007,7 +1041,8 @@ async function cmdWeb({ port, dir, open, dev }) {
1007
1041
  const raw = await readBody(req);
1008
1042
  const payload = raw ? JSON.parse(raw) : {};
1009
1043
 
1010
- const workspaceDir = path.resolve(process.cwd(), payload.dir || dir || './freya');
1044
+ const requestedDir = payload.dir || dir || './freya';
1045
+ const workspaceDir = normalizeWorkspaceDir(requestedDir);
1011
1046
 
1012
1047
  if (req.url === '/api/pick-dir') {
1013
1048
  const picked = await pickDirectoryNative();
@@ -1016,27 +1051,31 @@ async function cmdWeb({ port, dir, open, dev }) {
1016
1051
 
1017
1052
  if (req.url === '/api/init') {
1018
1053
  const pkg = '@cccarv82/freya';
1019
- const r = await run(guessNpxCmd(), [pkg, 'init', workspaceDir], process.cwd());
1020
- return safeJson(res, r.code === 0 ? 200 : 400, { output: (r.stdout + r.stderr).trim() });
1054
+ const r = await run(guessNpxCmd(), [guessNpxYesFlag(), pkg, 'init', workspaceDir], process.cwd());
1055
+ const output = (r.stdout + r.stderr).trim();
1056
+ return safeJson(res, r.code === 0 ? 200 : 400, r.code === 0 ? { output } : { error: output || 'init failed', output });
1021
1057
  }
1022
1058
 
1023
1059
  if (req.url === '/api/update') {
1024
1060
  const pkg = '@cccarv82/freya';
1025
1061
  fs.mkdirSync(workspaceDir, { recursive: true });
1026
- const r = await run(guessNpxCmd(), [pkg, 'init', '--here'], workspaceDir);
1027
- return safeJson(res, r.code === 0 ? 200 : 400, { output: (r.stdout + r.stderr).trim() });
1062
+ const r = await run(guessNpxCmd(), [guessNpxYesFlag(), pkg, 'init', '--here'], workspaceDir);
1063
+ const output = (r.stdout + r.stderr).trim();
1064
+ return safeJson(res, r.code === 0 ? 200 : 400, r.code === 0 ? { output } : { error: output || 'update failed', output });
1028
1065
  }
1029
1066
 
1030
1067
  const npmCmd = guessNpmCmd();
1031
1068
 
1032
1069
  if (req.url === '/api/health') {
1033
1070
  const r = await run(npmCmd, ['run', 'health'], workspaceDir);
1034
- return safeJson(res, r.code === 0 ? 200 : 400, { output: (r.stdout + r.stderr).trim() });
1071
+ const output = (r.stdout + r.stderr).trim();
1072
+ return safeJson(res, r.code === 0 ? 200 : 400, r.code === 0 ? { output } : { error: output || 'health failed', output });
1035
1073
  }
1036
1074
 
1037
1075
  if (req.url === '/api/migrate') {
1038
1076
  const r = await run(npmCmd, ['run', 'migrate'], workspaceDir);
1039
- return safeJson(res, r.code === 0 ? 200 : 400, { output: (r.stdout + r.stderr).trim() });
1077
+ const output = (r.stdout + r.stderr).trim();
1078
+ return safeJson(res, r.code === 0 ? 200 : 400, r.code === 0 ? { output } : { error: output || 'migrate failed', output });
1040
1079
  }
1041
1080
 
1042
1081
  if (req.url === '/api/report') {
@@ -1060,7 +1099,7 @@ async function cmdWeb({ port, dir, open, dev }) {
1060
1099
  // Prefer showing the actual report content when available.
1061
1100
  const output = reportText ? reportText : out;
1062
1101
 
1063
- return safeJson(res, r.code === 0 ? 200 : 400, { output, reportPath, reportText });
1102
+ return safeJson(res, r.code === 0 ? 200 : 400, r.code === 0 ? { output, reportPath, reportText } : { error: output || 'report failed', output, reportPath, reportText });
1064
1103
  }
1065
1104
 
1066
1105
  if (req.url === '/api/publish') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cccarv82/freya",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "description": "Personal AI Assistant with local-first persistence",
5
5
  "scripts": {
6
6
  "health": "node scripts/validate-data.js",