@specship/specship-win32-arm64 0.12.1 → 0.13.0
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/lib/dist/bin/specship.js +48 -12
- package/lib/dist/bin/specship.js.map +1 -1
- package/lib/dist/db/migrations.d.ts +1 -1
- package/lib/dist/db/migrations.d.ts.map +1 -1
- package/lib/dist/db/migrations.js +16 -1
- package/lib/dist/db/migrations.js.map +1 -1
- package/lib/dist/db/schema.sql +10 -0
- package/lib/dist/server/cli.js +9 -7
- package/lib/dist/server/product-version.js +44 -0
- package/lib/dist/server/routes/claude.js +153 -42
- package/lib/dist/server/routes/config.js +35 -0
- package/lib/dist/server/routes/graph.js +22 -1
- package/lib/dist/server/routes/mcp.js +510 -0
- package/lib/dist/server/routes/projects.js +31 -3
- package/lib/dist/server/routes/status.js +3 -0
- package/lib/dist/server/routes/workflow.js +29 -2
- package/lib/dist/server/server-config.js +38 -0
- package/lib/dist/server/server.js +128 -58
- package/lib/dist/ui/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
- package/lib/dist/ui/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
- package/lib/dist/ui/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
- package/lib/dist/ui/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
- package/lib/dist/ui/assets/geist-mono-cyrillic-ext-wght-normal-I4S5GZfc.woff2 +0 -0
- package/lib/dist/ui/assets/geist-mono-cyrillic-wght-normal-BmXc_FBt.woff2 +0 -0
- package/lib/dist/ui/assets/geist-mono-latin-ext-wght-normal-DrnZ1wKl.woff2 +0 -0
- package/lib/dist/ui/assets/geist-mono-latin-wght-normal-B_7UjwxQ.woff2 +0 -0
- package/lib/dist/ui/assets/geist-mono-symbols2-wght-normal-GZpp1pK2.woff2 +0 -0
- package/lib/dist/ui/assets/geist-mono-vietnamese-wght-normal-D8KDMBhC.woff2 +0 -0
- package/lib/dist/ui/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
- package/lib/dist/ui/assets/index-D8e4LiVb.js +51 -0
- package/lib/dist/ui/assets/index-ezwX2EfG.css +1 -0
- package/lib/dist/ui/favicon-16.png +0 -0
- package/lib/dist/ui/favicon-180.png +0 -0
- package/lib/dist/ui/favicon-32.png +0 -0
- package/lib/dist/ui/favicon.svg +20 -0
- package/lib/dist/ui/index.html +17 -0
- package/lib/dist/update/resolve-latest.d.ts +14 -0
- package/lib/dist/update/resolve-latest.d.ts.map +1 -0
- package/lib/dist/update/resolve-latest.js +29 -0
- package/lib/dist/update/resolve-latest.js.map +1 -0
- package/lib/dist/update/run-installer.d.ts +13 -0
- package/lib/dist/update/run-installer.d.ts.map +1 -0
- package/lib/dist/update/run-installer.js +43 -0
- package/lib/dist/update/run-installer.js.map +1 -0
- package/lib/dist/update/updater.d.ts +53 -0
- package/lib/dist/update/updater.d.ts.map +1 -0
- package/lib/dist/update/updater.js +164 -0
- package/lib/dist/update/updater.js.map +1 -0
- package/lib/dist/workflows/executor.d.ts.map +1 -1
- package/lib/dist/workflows/executor.js +4 -0
- package/lib/dist/workflows/executor.js.map +1 -1
- package/lib/dist/workflows/runners/prompt.d.ts +6 -0
- package/lib/dist/workflows/runners/prompt.d.ts.map +1 -1
- package/lib/dist/workflows/runners/prompt.js +10 -0
- package/lib/dist/workflows/runners/prompt.js.map +1 -1
- package/lib/dist/workflows/runners/types.d.ts +12 -0
- package/lib/dist/workflows/runners/types.d.ts.map +1 -1
- package/lib/node_modules/.package-lock.json +1 -1
- package/lib/package.json +3 -2
- package/package.json +1 -1
- package/lib/dist/server/ssr/md.mjs +0 -83
- package/lib/dist/server/ssr/public/app.css +0 -1
- package/lib/dist/server/ssr/render.mjs +0 -596
- package/lib/dist/server/ssr/routes.js +0 -73
|
@@ -33,6 +33,9 @@ import { registerReflectRoutes } from './routes/reflect.js';
|
|
|
33
33
|
import { registerMaintainabilityRoutes } from './routes/maintainability.js';
|
|
34
34
|
import { registerDomainRoutes } from './routes/domain.js';
|
|
35
35
|
import { registerChatRoutes } from './routes/chat.js';
|
|
36
|
+
import { registerMcpRoutes } from './routes/mcp.js';
|
|
37
|
+
import { registerConfigRoutes } from './routes/config.js';
|
|
38
|
+
import { readServerConfig } from './server-config.js';
|
|
36
39
|
/**
|
|
37
40
|
* Lazy-import the specship core. Two delivery shapes are supported:
|
|
38
41
|
*
|
|
@@ -43,11 +46,37 @@ import { registerChatRoutes } from './routes/chat.js';
|
|
|
43
46
|
* resolution finding `@specship/specship` (it won't, the
|
|
44
47
|
* bundle stages a single package by name `@specship/specship`).
|
|
45
48
|
*
|
|
46
|
-
* 2. **Workspace / dev** — `
|
|
47
|
-
* the workspace `file
|
|
49
|
+
* 2. **Workspace / dev** — `server/dist/server.js` imports via
|
|
50
|
+
* the workspace `file:..` dep. Falls through to the named import.
|
|
48
51
|
*
|
|
49
52
|
* The function caches the resolved module so the lookup only runs once.
|
|
50
53
|
*/
|
|
54
|
+
/**
|
|
55
|
+
* Locate the built desktop SPA (REQ-DESKTOP-017.A1) when the caller didn't
|
|
56
|
+
* pass an explicit `webDir`. Two delivery shapes, probed relative to this
|
|
57
|
+
* file so cwd never matters:
|
|
58
|
+
*
|
|
59
|
+
* 1. **Bundled** — `build-server-bundle.mjs` copies `ui/dist/**` to the
|
|
60
|
+
* root `dist/ui/`, which the platform tarball ships as
|
|
61
|
+
* `<bundle>/lib/dist/ui/` next to `dist/server/` (this file).
|
|
62
|
+
* 2. **Workspace / dev** — `server/{src,dist}/server.*` →
|
|
63
|
+
* repo root → `ui/dist/` (present only after `cd ui && npm run build`).
|
|
64
|
+
*
|
|
65
|
+
* Returns null when neither exists — the server then simply has no SPA
|
|
66
|
+
* mount, same as `webDir: null`.
|
|
67
|
+
*/
|
|
68
|
+
export function resolveDefaultWebDir() {
|
|
69
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
70
|
+
const candidates = [
|
|
71
|
+
path.resolve(here, '..', 'ui'), // bundled: dist/server → dist/ui
|
|
72
|
+
path.resolve(here, '..', '..', 'ui', 'dist'), // workspace: server/dist → <root>/ui/dist
|
|
73
|
+
];
|
|
74
|
+
for (const dir of candidates) {
|
|
75
|
+
if (existsSync(path.join(dir, 'index.html')))
|
|
76
|
+
return dir;
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
51
80
|
let cachedSpecShip = null;
|
|
52
81
|
async function loadSpecShip() {
|
|
53
82
|
if (cachedSpecShip)
|
|
@@ -110,59 +139,102 @@ export async function createServer(options) {
|
|
|
110
139
|
// provided one or explicitly opted out. Hooks into the primary project's
|
|
111
140
|
// SQLite handle — when there's no primary, analytics endpoints will return
|
|
112
141
|
// 409 until one is set (typically the next server restart with -p).
|
|
142
|
+
//
|
|
143
|
+
// The persisted runtime config (~/.specship/server-config.json) gates the
|
|
144
|
+
// start too: the Settings ingest toggle must survive a restart
|
|
145
|
+
// (REQ-DESKTOP-028.A2). CLI `--no-ingest` still wins over everything.
|
|
113
146
|
let watcher = options.watcher ?? null;
|
|
114
147
|
let ownedWatcher = false;
|
|
115
|
-
|
|
148
|
+
let ingestEnabled = watcher != null
|
|
149
|
+
|| (options.ingest !== false && readServerConfig().ingestEnabled !== false);
|
|
150
|
+
const startIngestWatcher = () => {
|
|
151
|
+
if (watcher)
|
|
152
|
+
return true;
|
|
153
|
+
if (!primaryCg)
|
|
154
|
+
return false;
|
|
116
155
|
const cgAny = primaryCg;
|
|
117
156
|
const dbHandle = cgAny.db?.getDb ? cgAny.db.getDb() : cgAny.queries?.db;
|
|
118
|
-
if (dbHandle)
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
157
|
+
if (!dbHandle)
|
|
158
|
+
return false;
|
|
159
|
+
// Build a sync resolveGraph: for the primary project path return the
|
|
160
|
+
// already-open SpecShip instance (which satisfies GraphLike).
|
|
161
|
+
// Sessions from other project paths resolve to null — they'll be left
|
|
162
|
+
// as 'unresolved' and retried on the next boot when that project is primary.
|
|
163
|
+
// The stored project_path is the lossy-decoded slug (every '-' → '/'), so
|
|
164
|
+
// an exact compare against the real primaryPath never matched and savings
|
|
165
|
+
// stayed 0. primaryProjectMatcher accepts both the real path and its
|
|
166
|
+
// mangled stored form. Sessions from OTHER projects still resolve to null
|
|
167
|
+
// (left 'unresolved', retried when that project is primary).
|
|
168
|
+
const primaryPath = options.projectRoot ?? null;
|
|
169
|
+
const isPrimary = primaryPath ? primaryProjectMatcher(primaryPath) : () => false;
|
|
170
|
+
const resolveGraph = (projectPath) => primaryPath && isPrimary(projectPath) ? primaryCg : null;
|
|
171
|
+
watcher = startWatcher(dbHandle, { verbose, resolveGraph });
|
|
172
|
+
ownedWatcher = true;
|
|
173
|
+
if (verbose)
|
|
174
|
+
console.error('[specship-server] JSONL ingest watcher started');
|
|
175
|
+
// Backfill displaced_files / resolution for pre-upgrade rows (is_specship=1,
|
|
176
|
+
// resolution IS NULL). Idempotent — safe to run on every boot. Non-fatal:
|
|
177
|
+
// a failure here must never abort server startup.
|
|
178
|
+
try {
|
|
179
|
+
backfillDisplaced(dbHandle, resolveGraph);
|
|
133
180
|
if (verbose)
|
|
134
|
-
console.error('[specship-server]
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
console.error(
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
181
|
+
console.error('[specship-server] specship-impact backfill complete');
|
|
182
|
+
}
|
|
183
|
+
catch (err) {
|
|
184
|
+
console.error('[specship-server] specship-impact backfill failed (non-fatal):', err instanceof Error ? err.message : String(err));
|
|
185
|
+
}
|
|
186
|
+
// Re-cost prompts ingested while their model family was unpriced,
|
|
187
|
+
// e.g. fable sessions stuck at $0 (REQ-DASHINT-001.A2). Idempotent.
|
|
188
|
+
try {
|
|
189
|
+
const recosted = recostUnpricedPrompts(dbHandle);
|
|
190
|
+
if (verbose && recosted > 0)
|
|
191
|
+
console.error(`[specship-server] re-costed ${recosted} unpriced prompts`);
|
|
192
|
+
}
|
|
193
|
+
catch (err) {
|
|
194
|
+
console.error('[specship-server] pricing backfill failed (non-fatal):', err instanceof Error ? err.message : String(err));
|
|
195
|
+
}
|
|
196
|
+
return true;
|
|
197
|
+
};
|
|
198
|
+
if (!watcher && ingestEnabled) {
|
|
199
|
+
if (primaryCg) {
|
|
200
|
+
startIngestWatcher();
|
|
201
|
+
}
|
|
202
|
+
else if (verbose) {
|
|
203
|
+
console.error('[specship-server] no primary project — analytics will be empty until one is set');
|
|
156
204
|
}
|
|
157
|
-
}
|
|
158
|
-
else if (!primaryCg && options.ingest !== false && verbose) {
|
|
159
|
-
console.error('[specship-server] no primary project — analytics will be empty until one is set');
|
|
160
205
|
}
|
|
161
206
|
// Decorate the Fastify instance so route handlers can access shared state
|
|
162
207
|
// without globals or DI containers.
|
|
163
208
|
app.decorate('projects', registry);
|
|
164
209
|
app.decorate('primaryCg', primaryCg);
|
|
165
210
|
app.decorate('watcher', watcher);
|
|
211
|
+
// Runtime ingest control for GET/PUT /api/config (REQ-DESKTOP-028.A2).
|
|
212
|
+
// `enabled` is the configured intent (round-trips even when no primary
|
|
213
|
+
// project can host a watcher yet); `active` is whether one is running.
|
|
214
|
+
// start/stop keep `app.watcher` in sync so /api/refresh always sees the
|
|
215
|
+
// live handle.
|
|
216
|
+
app.decorate('ingestControl', {
|
|
217
|
+
enabled: () => ingestEnabled,
|
|
218
|
+
active: () => watcher != null,
|
|
219
|
+
start: () => {
|
|
220
|
+
ingestEnabled = true;
|
|
221
|
+
const started = startIngestWatcher();
|
|
222
|
+
app.watcher = watcher;
|
|
223
|
+
return started;
|
|
224
|
+
},
|
|
225
|
+
stop: () => {
|
|
226
|
+
ingestEnabled = false;
|
|
227
|
+
if (watcher) {
|
|
228
|
+
try {
|
|
229
|
+
watcher.stop();
|
|
230
|
+
}
|
|
231
|
+
catch { /* ignore */ }
|
|
232
|
+
}
|
|
233
|
+
watcher = null;
|
|
234
|
+
ownedWatcher = false;
|
|
235
|
+
app.watcher = null;
|
|
236
|
+
},
|
|
237
|
+
});
|
|
166
238
|
// activeCg(req): resolve `?project=<slug>` → SpecShip instance, with the
|
|
167
239
|
// primary as fallback. Returns null when nothing is selectable; route
|
|
168
240
|
// handlers respond 409 in that case.
|
|
@@ -193,19 +265,17 @@ export async function createServer(options) {
|
|
|
193
265
|
await registerMaintainabilityRoutes(app);
|
|
194
266
|
await registerDomainRoutes(app);
|
|
195
267
|
await registerChatRoutes(app);
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
//
|
|
199
|
-
//
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
if (options.webDir) {
|
|
208
|
-
const indexPath = path.join(options.webDir, 'index.html');
|
|
268
|
+
await registerMcpRoutes(app);
|
|
269
|
+
await registerConfigRoutes(app);
|
|
270
|
+
// Serve the built desktop SPA (the `ui/` module) from `webDir` and fall back
|
|
271
|
+
// to index.html for client-side routes. This is the dashboard's only surface
|
|
272
|
+
// (REQ-DESKTOP-033 — the server-rendered dashboard retired). Must register
|
|
273
|
+
// AFTER the /api/* routes so they take precedence over the SPA wildcard. When
|
|
274
|
+
// the caller passed no webDir at all, auto-detect the SPA shipped next to the
|
|
275
|
+
// server build; `null` explicitly opts out (headless / API only).
|
|
276
|
+
const webDir = options.webDir === undefined ? resolveDefaultWebDir() : options.webDir;
|
|
277
|
+
if (webDir) {
|
|
278
|
+
const indexPath = path.join(webDir, 'index.html');
|
|
209
279
|
let indexBuffer = null;
|
|
210
280
|
try {
|
|
211
281
|
indexBuffer = await fs.readFile(indexPath);
|
|
@@ -217,7 +287,7 @@ export async function createServer(options) {
|
|
|
217
287
|
}
|
|
218
288
|
if (indexBuffer) {
|
|
219
289
|
const cachedIndex = indexBuffer;
|
|
220
|
-
const serveStatic = makeStaticHandler(
|
|
290
|
+
const serveStatic = makeStaticHandler(webDir);
|
|
221
291
|
// A single 404 handler does both jobs:
|
|
222
292
|
// 1. Try the requested URL as a real file under webDir → serve it
|
|
223
293
|
// with the right content-type. Covers the SPA's chunk-*.js,
|
|
@@ -250,7 +320,7 @@ export async function createServer(options) {
|
|
|
250
320
|
reply.code(200).type('text/html').send(cachedIndex);
|
|
251
321
|
});
|
|
252
322
|
if (verbose)
|
|
253
|
-
console.error(`[specship-server] serving SPA from ${
|
|
323
|
+
console.error(`[specship-server] serving SPA from ${webDir}`);
|
|
254
324
|
}
|
|
255
325
|
}
|
|
256
326
|
await app.listen({ host, port });
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|