@medicine-wheel/app 0.2.3
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/Dockerfile +69 -0
- package/LICENSE +205 -0
- package/README.md +201 -0
- package/app/accountability/page.tsx +95 -0
- package/app/api/ceremonies/route.ts +52 -0
- package/app/api/directions/route.ts +6 -0
- package/app/api/edges/route.ts +27 -0
- package/app/api/health/route.ts +37 -0
- package/app/api/narrative/beats/route.ts +29 -0
- package/app/api/narrative/cycles/route.ts +23 -0
- package/app/api/nodes/route.ts +52 -0
- package/app/api/resources/route.ts +48 -0
- package/app/ceremonies/page.tsx +161 -0
- package/app/globals.css +68 -0
- package/app/graph/page.tsx +200 -0
- package/app/layout.tsx +24 -0
- package/app/narrative/beats/page.tsx +145 -0
- package/app/narrative/cycles/page.tsx +143 -0
- package/app/narrative/page.tsx +113 -0
- package/app/nodes/page.tsx +199 -0
- package/app/page.tsx +148 -0
- package/app/relations/page.tsx +191 -0
- package/components/direction-panel.tsx +96 -0
- package/components/navigation.tsx +105 -0
- package/components/theme-provider.tsx +11 -0
- package/components/workspaces-panel.tsx +110 -0
- package/dist/cli/mw.js +731 -0
- package/dist/cli/mwsrv.js +267 -0
- package/docker-build-push.sh +15 -0
- package/docker-entrypoint.sh +26 -0
- package/lib/jsonl-store.ts +586 -0
- package/lib/store.ts +226 -0
- package/lib/types.ts +23 -0
- package/lib/utils.ts +6 -0
- package/next-env.d.ts +6 -0
- package/next.config.mjs +5 -0
- package/package.json +112 -0
- package/postcss.config.mjs +6 -0
- package/public/fonts/Stereohead.otf +0 -0
- package/tsconfig.json +21 -0
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* mwsrv ā Medicine Wheel Server Launcher
|
|
5
|
+
*
|
|
6
|
+
* Starts the Medicine Wheel Next.js application in local or Docker mode.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* mwsrv [options]
|
|
10
|
+
* mwsrv --docker -D /path/to/project
|
|
11
|
+
*
|
|
12
|
+
* Options:
|
|
13
|
+
* --port, -p <port> Port to listen on (default: 3940)
|
|
14
|
+
* --directory, -D <dir> Host directory containing (or for) .mw/store (default: cwd)
|
|
15
|
+
* --docker Run in Docker container
|
|
16
|
+
* --pull Pull Docker image before starting
|
|
17
|
+
* --no-open Do not open browser automatically
|
|
18
|
+
* --help, -h Show this help message
|
|
19
|
+
*/
|
|
20
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
21
|
+
if (k2 === undefined) k2 = k;
|
|
22
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
23
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
24
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
25
|
+
}
|
|
26
|
+
Object.defineProperty(o, k2, desc);
|
|
27
|
+
}) : (function(o, m, k, k2) {
|
|
28
|
+
if (k2 === undefined) k2 = k;
|
|
29
|
+
o[k2] = m[k];
|
|
30
|
+
}));
|
|
31
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
32
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
33
|
+
}) : function(o, v) {
|
|
34
|
+
o["default"] = v;
|
|
35
|
+
});
|
|
36
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
37
|
+
var ownKeys = function(o) {
|
|
38
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
39
|
+
var ar = [];
|
|
40
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
41
|
+
return ar;
|
|
42
|
+
};
|
|
43
|
+
return ownKeys(o);
|
|
44
|
+
};
|
|
45
|
+
return function (mod) {
|
|
46
|
+
if (mod && mod.__esModule) return mod;
|
|
47
|
+
var result = {};
|
|
48
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
49
|
+
__setModuleDefault(result, mod);
|
|
50
|
+
return result;
|
|
51
|
+
};
|
|
52
|
+
})();
|
|
53
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
54
|
+
const child_process_1 = require("child_process");
|
|
55
|
+
const path = __importStar(require("path"));
|
|
56
|
+
const fs = __importStar(require("fs"));
|
|
57
|
+
const DOCKER_IMAGE = 'jgwill/medicine-wheel:app';
|
|
58
|
+
const DEFAULT_PORT = 3940;
|
|
59
|
+
const CONTAINER_PORT = 3940;
|
|
60
|
+
function parseArgs(argv) {
|
|
61
|
+
const args = argv.slice(2);
|
|
62
|
+
const flags = {};
|
|
63
|
+
const positional = [];
|
|
64
|
+
for (let i = 0; i < args.length; i++) {
|
|
65
|
+
const arg = args[i];
|
|
66
|
+
if (arg === '--') {
|
|
67
|
+
positional.push(...args.slice(i + 1));
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
else if (arg.startsWith('--')) {
|
|
71
|
+
const key = arg.slice(2);
|
|
72
|
+
const next = args[i + 1];
|
|
73
|
+
if (next !== undefined && !next.startsWith('-')) {
|
|
74
|
+
flags[key] = args[++i];
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
flags[key] = true;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
else if (arg.startsWith('-') && arg.length === 2) {
|
|
81
|
+
const key = arg.slice(1);
|
|
82
|
+
const next = args[i + 1];
|
|
83
|
+
if (next !== undefined && !next.startsWith('-')) {
|
|
84
|
+
flags[key] = args[++i];
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
flags[key] = true;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
positional.push(arg);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return { flags, positional };
|
|
95
|
+
}
|
|
96
|
+
// āā Helpers āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
97
|
+
function resolvePackageRoot() {
|
|
98
|
+
try {
|
|
99
|
+
const scriptDir = path.dirname(fs.realpathSync(process.argv[1]));
|
|
100
|
+
// dist/cli/mwsrv.js ā go up two levels to package root
|
|
101
|
+
return path.resolve(scriptDir, '..', '..');
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
return process.cwd();
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function openBrowser(url) {
|
|
108
|
+
const cmd = process.platform === 'darwin'
|
|
109
|
+
? 'open'
|
|
110
|
+
: process.platform === 'win32'
|
|
111
|
+
? 'start'
|
|
112
|
+
: 'xdg-open';
|
|
113
|
+
(0, child_process_1.spawn)(cmd, [url], { detached: true, stdio: 'ignore' }).unref();
|
|
114
|
+
}
|
|
115
|
+
// āā Docker mode āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
116
|
+
async function runDocker(opts) {
|
|
117
|
+
const { hostDir, port, pull, noOpen } = opts;
|
|
118
|
+
// Resolve store path on host: <dir>/.mw/store
|
|
119
|
+
const storeDir = path.resolve(hostDir, '.mw', 'store');
|
|
120
|
+
fs.mkdirSync(storeDir, { recursive: true });
|
|
121
|
+
if (pull) {
|
|
122
|
+
console.log(`ā¬ļø Pulling ${DOCKER_IMAGE}...`);
|
|
123
|
+
await new Promise((resolve, reject) => {
|
|
124
|
+
const p = (0, child_process_1.spawn)('docker', ['pull', DOCKER_IMAGE], { stdio: 'inherit' });
|
|
125
|
+
p.on('exit', (code) => code === 0 ? resolve() : reject(new Error(`docker pull failed: ${code}`)));
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
const dockerArgs = [
|
|
129
|
+
'run', '--rm',
|
|
130
|
+
'-p', `${port}:${CONTAINER_PORT}`,
|
|
131
|
+
'-v', `${storeDir}:/data/store`,
|
|
132
|
+
'-e', `MW_DATA_DIR=/data/store`,
|
|
133
|
+
'-e', `MW_STORAGE_PROVIDER=jsonl`,
|
|
134
|
+
'-e', `PORT=${CONTAINER_PORT}`,
|
|
135
|
+
DOCKER_IMAGE,
|
|
136
|
+
];
|
|
137
|
+
console.log(`š³ docker ${dockerArgs.join(' ')}\n`);
|
|
138
|
+
const proc = (0, child_process_1.spawn)('docker', dockerArgs, { stdio: 'inherit' });
|
|
139
|
+
if (!noOpen) {
|
|
140
|
+
setTimeout(() => {
|
|
141
|
+
const url = `http://localhost:${port}`;
|
|
142
|
+
console.log(`\nš Opening browser: ${url}\n`);
|
|
143
|
+
openBrowser(url);
|
|
144
|
+
}, 4000);
|
|
145
|
+
}
|
|
146
|
+
const shutdown = () => {
|
|
147
|
+
console.log('\nš Shutting down Medicine Wheel...');
|
|
148
|
+
proc.kill();
|
|
149
|
+
process.exit(0);
|
|
150
|
+
};
|
|
151
|
+
process.on('SIGINT', shutdown);
|
|
152
|
+
process.on('SIGTERM', shutdown);
|
|
153
|
+
proc.on('exit', (code) => process.exit(code ?? 0));
|
|
154
|
+
}
|
|
155
|
+
// āā Local mode āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
156
|
+
function runLocal(opts) {
|
|
157
|
+
const { hostDir, port, noOpen, packageRoot } = opts;
|
|
158
|
+
const storeDir = path.resolve(hostDir, '.mw', 'store');
|
|
159
|
+
fs.mkdirSync(storeDir, { recursive: true });
|
|
160
|
+
// Local mode always runs `next dev`. `next start` requires a production build
|
|
161
|
+
// (.next/) which is not shipped in the published package. For a
|
|
162
|
+
// production-like experience without a source checkout, use --docker instead.
|
|
163
|
+
const nextCmd = 'dev';
|
|
164
|
+
console.log(`šæ Medicine Wheel ā local dev mode (next dev)`);
|
|
165
|
+
console.log(` For a production-built server use: mwsrv --docker`);
|
|
166
|
+
console.log(`š Store: ${storeDir}`);
|
|
167
|
+
console.log(`š http://localhost:${port}\n`);
|
|
168
|
+
const env = {
|
|
169
|
+
...process.env,
|
|
170
|
+
MW_DATA_DIR: storeDir,
|
|
171
|
+
MW_STORAGE_PROVIDER: process.env.MW_STORAGE_PROVIDER ?? 'jsonl',
|
|
172
|
+
PORT: String(port),
|
|
173
|
+
};
|
|
174
|
+
// next binary may be in node_modules/.bin relative to package root
|
|
175
|
+
const nextBin = path.join(packageRoot, 'node_modules', '.bin', 'next');
|
|
176
|
+
const nextExe = fs.existsSync(nextBin) ? nextBin : 'next';
|
|
177
|
+
const proc = (0, child_process_1.spawn)(nextExe, [nextCmd, '-p', String(port)], {
|
|
178
|
+
cwd: packageRoot,
|
|
179
|
+
stdio: 'inherit',
|
|
180
|
+
env,
|
|
181
|
+
});
|
|
182
|
+
if (!noOpen) {
|
|
183
|
+
setTimeout(() => {
|
|
184
|
+
const url = `http://localhost:${port}`;
|
|
185
|
+
console.log(`\nš Opening browser: ${url}\n`);
|
|
186
|
+
openBrowser(url);
|
|
187
|
+
}, 5000);
|
|
188
|
+
}
|
|
189
|
+
const shutdown = () => {
|
|
190
|
+
console.log('\nš Shutting down Medicine Wheel...');
|
|
191
|
+
proc.kill();
|
|
192
|
+
process.exit(0);
|
|
193
|
+
};
|
|
194
|
+
process.on('SIGINT', shutdown);
|
|
195
|
+
process.on('SIGTERM', shutdown);
|
|
196
|
+
proc.on('exit', (code) => process.exit(code ?? 0));
|
|
197
|
+
}
|
|
198
|
+
// āā Help āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
199
|
+
function showHelp() {
|
|
200
|
+
console.log(`
|
|
201
|
+
\x1b[1mšæ mwsrv ā Medicine Wheel Server Launcher\x1b[0m
|
|
202
|
+
|
|
203
|
+
DESCRIPTION
|
|
204
|
+
Starts the Medicine Wheel Next.js application locally or in Docker.
|
|
205
|
+
In Docker mode, data persists in the host directory's .mw/store folder.
|
|
206
|
+
|
|
207
|
+
USAGE
|
|
208
|
+
mwsrv [options]
|
|
209
|
+
|
|
210
|
+
OPTIONS
|
|
211
|
+
--port, -p <port> Port to listen on (default: ${DEFAULT_PORT})
|
|
212
|
+
--directory, -D <dir> Host directory for .mw/store (default: current dir)
|
|
213
|
+
--docker Run in Docker container (image: ${DOCKER_IMAGE})
|
|
214
|
+
--pull Pull/update Docker image before starting
|
|
215
|
+
--no-open Do not auto-open browser
|
|
216
|
+
--help, -h Show this help
|
|
217
|
+
|
|
218
|
+
EXAMPLES
|
|
219
|
+
# Start locally (uses current directory's .mw/store)
|
|
220
|
+
mwsrv
|
|
221
|
+
|
|
222
|
+
# Start locally with a specific data directory
|
|
223
|
+
mwsrv -D ~/my-research
|
|
224
|
+
|
|
225
|
+
# Start in Docker (recommended for clean environments)
|
|
226
|
+
mwsrv --docker
|
|
227
|
+
|
|
228
|
+
# Docker with custom directory and port
|
|
229
|
+
mwsrv --docker -D ~/my-research --port 4000
|
|
230
|
+
|
|
231
|
+
# Pull latest image then start in Docker
|
|
232
|
+
mwsrv --docker --pull -D ~/my-research
|
|
233
|
+
|
|
234
|
+
ENVIRONMENT
|
|
235
|
+
MW_API_URL Client API URL (default: http://localhost:${DEFAULT_PORT})
|
|
236
|
+
MW_STORAGE_PROVIDER Storage backend: jsonl (default) or postgres
|
|
237
|
+
DATABASE_URL PostgreSQL connection string (when using postgres provider)
|
|
238
|
+
`);
|
|
239
|
+
}
|
|
240
|
+
// āā Main āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
241
|
+
async function main() {
|
|
242
|
+
const { flags } = parseArgs(process.argv);
|
|
243
|
+
if (flags['help'] || flags['h']) {
|
|
244
|
+
showHelp();
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
const port = Number(flags['port'] ?? flags['p'] ?? DEFAULT_PORT);
|
|
248
|
+
const directory = String(flags['directory'] ?? flags['D'] ?? process.cwd());
|
|
249
|
+
const useDocker = Boolean(flags['docker']);
|
|
250
|
+
const pull = Boolean(flags['pull']);
|
|
251
|
+
const noOpen = Boolean(flags['no-open']);
|
|
252
|
+
const hostDir = path.resolve(directory);
|
|
253
|
+
console.log(`šæ Medicine Wheel${useDocker ? ' [DOCKER]' : ' [LOCAL]'}`);
|
|
254
|
+
console.log(`š Directory: ${hostDir}`);
|
|
255
|
+
console.log(`š Port: ${port}\n`);
|
|
256
|
+
if (useDocker) {
|
|
257
|
+
await runDocker({ hostDir, port, pull, noOpen });
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
const packageRoot = resolvePackageRoot();
|
|
261
|
+
runLocal({ hostDir, port, noOpen, packageRoot });
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
main().catch((err) => {
|
|
265
|
+
console.error('ā Fatal error:', err instanceof Error ? err.message : String(err));
|
|
266
|
+
process.exit(1);
|
|
267
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
set -e
|
|
3
|
+
|
|
4
|
+
TAG="${1:-app}"
|
|
5
|
+
IMAGE="jgwill/medicine-wheel:${TAG}"
|
|
6
|
+
|
|
7
|
+
echo "š³ Building ${IMAGE}..."
|
|
8
|
+
docker build -t "${IMAGE}" .
|
|
9
|
+
|
|
10
|
+
echo "š¤ Pushing ${IMAGE}..."
|
|
11
|
+
docker push "${IMAGE}"
|
|
12
|
+
|
|
13
|
+
echo "ā
Done: ${IMAGE}"
|
|
14
|
+
echo "Run with:"
|
|
15
|
+
echo " docker run --rm -p 3940:3940 -v /abs/path/to/project/.mw/store:/data/store ${IMAGE}"
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
set -e
|
|
3
|
+
|
|
4
|
+
# Medicine Wheel Docker Entrypoint
|
|
5
|
+
#
|
|
6
|
+
# The Next.js app is pre-built. Data store is mounted at /data/store.
|
|
7
|
+
# Environment variables:
|
|
8
|
+
# MW_DATA_DIR ā path to JSONL store directory (default: /data/store)
|
|
9
|
+
# MW_STORAGE_PROVIDER ā storage backend (default: jsonl)
|
|
10
|
+
# PORT ā HTTP port (default: 3940)
|
|
11
|
+
|
|
12
|
+
STORE_DIR="${MW_DATA_DIR:-/data/store}"
|
|
13
|
+
PORT="${PORT:-3940}"
|
|
14
|
+
|
|
15
|
+
mkdir -p "$STORE_DIR"
|
|
16
|
+
|
|
17
|
+
echo "šæ Medicine Wheel"
|
|
18
|
+
echo "š Store: $STORE_DIR"
|
|
19
|
+
echo "š http://localhost:$PORT"
|
|
20
|
+
echo ""
|
|
21
|
+
|
|
22
|
+
export MW_DATA_DIR="$STORE_DIR"
|
|
23
|
+
export MW_STORAGE_PROVIDER="${MW_STORAGE_PROVIDER:-jsonl}"
|
|
24
|
+
export PORT="$PORT"
|
|
25
|
+
|
|
26
|
+
exec node_modules/.bin/next start -p "$PORT"
|