@exellix/jobs-ui 1.0.1 → 2.0.1

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.
@@ -0,0 +1,14 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Exellix Jobs</title>
7
+ <link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>⚙</text></svg>" />
8
+ <script type="module" crossorigin src="/assets/index-D_JRmMIk.js"></script>
9
+ <link rel="stylesheet" crossorigin href="/assets/index-CUeGAvCJ.css">
10
+ </head>
11
+ <body>
12
+ <div id="root"></div>
13
+ </body>
14
+ </html>
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@exellix/jobs-ui",
3
- "version": "1.0.1",
4
- "description": "Read-only job queue queries and optional HTTP serve CLI over @exellix/exellix-db.",
3
+ "version": "2.0.1",
4
+ "description": "Exellix jobs operator dashboard — React SPA for queue views, record-centric workflows, and work-factory wizards. Talks to @exellix/jobs-api over HTTP.",
5
5
  "type": "module",
6
6
  "engines": {
7
7
  "node": ">=20"
@@ -10,48 +10,46 @@
10
10
  "access": "public",
11
11
  "registry": "https://registry.npmjs.org/"
12
12
  },
13
- "main": "./dist/index.js",
14
- "types": "./dist/index.d.ts",
15
- "exports": {
16
- ".": {
17
- "types": "./dist/index.d.ts",
18
- "import": "./dist/index.js"
19
- },
20
- "./cli": {
21
- "types": "./dist/cli.d.ts",
22
- "import": "./dist/cli.js"
23
- }
24
- },
25
- "bin": {
26
- "exellix-jobs-ui": "./dist/cli.js"
27
- },
28
13
  "files": [
29
14
  "dist",
30
15
  "README.md"
31
16
  ],
32
17
  "scripts": {
33
- "build": "tsc -p tsconfig.build.json",
18
+ "build": "vite build",
34
19
  "clean": "node -e \"import('node:fs').then(fs=>fs.promises.rm('dist',{recursive:true,force:true}))\"",
35
20
  "prepack": "npm run build",
36
- "ui:serve": "node dist/cli.js serve",
21
+ "dev": "vite",
22
+ "kill-port": "node ../scripts/kill-port.mjs",
23
+ "predev": "npm run kill-port",
37
24
  "test": "vitest run",
38
- "test:live": "vitest run test/queries.live.spec.ts",
39
25
  "test:watch": "vitest"
40
26
  },
41
27
  "dependencies": {
42
- "@exellix/exellix-db": "^1.0.0",
43
- "fastify": "^5.8.5"
28
+ "@tanstack/react-query": "^5.101.0",
29
+ "@x12i/memorix-agents": "^1.2.0",
30
+ "react": "^19.2.7",
31
+ "react-dom": "^19.2.7",
32
+ "react-router-dom": "^7.17.0"
44
33
  },
45
34
  "devDependencies": {
46
35
  "@types/node": "^22.10.2",
36
+ "@types/react": "^19.2.17",
37
+ "@types/react-dom": "^19.2.3",
38
+ "@vitejs/plugin-react": "^5.2.0",
47
39
  "typescript": "^5.7.2",
40
+ "vite": "^6.4.3",
48
41
  "vitest": "^3.0.0"
49
42
  },
50
43
  "keywords": [
51
44
  "exellix",
52
45
  "jobs",
53
- "read-tier",
54
- "dashboard"
46
+ "dashboard",
47
+ "react"
55
48
  ],
56
- "license": "exellix-license"
49
+ "license": "exellix-license",
50
+ "repository": {
51
+ "type": "git",
52
+ "url": "git+ssh://git@github.com/exellix/exellix-engine-mono-repo.git",
53
+ "directory": "jobs-ui"
54
+ }
57
55
  }
package/dist/cli.d.ts DELETED
@@ -1,3 +0,0 @@
1
- #!/usr/bin/env node
2
- export {};
3
- //# sourceMappingURL=cli.d.ts.map
package/dist/cli.d.ts.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js DELETED
@@ -1,17 +0,0 @@
1
- #!/usr/bin/env node
2
- import { startJobsUiHttp } from './http/server.js';
3
- const cmd = process.argv[2];
4
- async function main() {
5
- if (cmd === 'serve') {
6
- const { url } = await startJobsUiHttp();
7
- console.log(`jobs-ui listening on ${url}`);
8
- return;
9
- }
10
- console.log('Usage: exellix-jobs-ui serve');
11
- process.exit(cmd ? 1 : 0);
12
- }
13
- main().catch((err) => {
14
- console.error(err);
15
- process.exit(1);
16
- });
17
- //# sourceMappingURL=cli.js.map
package/dist/cli.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEnD,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAE5B,KAAK,UAAU,IAAI;IACjB,IAAI,GAAG,KAAK,OAAO,EAAE,CAAC;QACpB,MAAM,EAAE,GAAG,EAAE,GAAG,MAAM,eAAe,EAAE,CAAC;QACxC,OAAO,CAAC,GAAG,CAAC,wBAAwB,GAAG,EAAE,CAAC,CAAC;QAC3C,OAAO;IACT,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAC;IAC5C,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC5B,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACnB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -1,13 +0,0 @@
1
- import type { JobRunStore } from '@exellix/exellix-db';
2
- export type StartJobsUiHttpOptions = {
3
- store?: JobRunStore;
4
- mongoUri?: string;
5
- port?: number;
6
- host?: string;
7
- };
8
- export declare function startJobsUiHttp(options?: StartJobsUiHttpOptions): Promise<{
9
- store: JobRunStore;
10
- close: () => Promise<void>;
11
- url: string;
12
- }>;
13
- //# sourceMappingURL=server.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/http/server.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAGvD,MAAM,MAAM,sBAAsB,GAAG;IACnC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,wBAAsB,eAAe,CAAC,OAAO,GAAE,sBAA2B,GAAG,OAAO,CAAC;IACnF,KAAK,EAAE,WAAW,CAAC;IACnB,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,GAAG,EAAE,MAAM,CAAC;CACb,CAAC,CA6CD"}
@@ -1,43 +0,0 @@
1
- import Fastify from 'fastify';
2
- import { createJobRunStore } from '@exellix/exellix-db';
3
- import { countByStatus, getRun, groupByItem, listRuns } from '../queries.js';
4
- export async function startJobsUiHttp(options = {}) {
5
- const bundle = options.store
6
- ? { store: options.store, close: async () => undefined }
7
- : await createJobRunStore({ mongoUri: options.mongoUri });
8
- const app = Fastify({ logger: false });
9
- const { store } = bundle;
10
- app.get('/health', async () => ({ ok: true }));
11
- app.get('/runs', async (req) => {
12
- const q = req.query;
13
- return listRuns(store, {
14
- jobDefId: q.jobDefId,
15
- itemId: q.itemId,
16
- graphId: q.graphId,
17
- status: q.status,
18
- page: q.page ? Number(q.page) : undefined,
19
- pageSize: q.pageSize ? Number(q.pageSize) : undefined,
20
- });
21
- });
22
- app.get('/runs/:id', async (req, reply) => {
23
- const { id } = req.params;
24
- const run = await getRun(store, id);
25
- if (!run) {
26
- return reply.code(404).send({ error: 'job run not found' });
27
- }
28
- return run;
29
- });
30
- app.get('/items/:itemId/runs', async (req) => {
31
- const { itemId } = req.params;
32
- return groupByItem(store, itemId);
33
- });
34
- app.get('/stats/by-status', async (req) => {
35
- const q = req.query;
36
- return countByStatus(store, q.jobDefId);
37
- });
38
- const port = options.port ?? Number(process.env.PORT ?? 3099);
39
- const host = options.host ?? '0.0.0.0';
40
- await app.listen({ port, host });
41
- return { store, close: async () => { await app.close(); await bundle.close(); }, url: `http://${host}:${port}` };
42
- }
43
- //# sourceMappingURL=server.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/http/server.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts DELETED
@@ -1,5 +0,0 @@
1
- export { countByStatus, countRuns, getRun, groupByItem, listRuns, type ListRunsQuery, type RunPage, type StatusCounts, } from './queries.js';
2
- export { startJobsUiHttp, type StartJobsUiHttpOptions } from './http/server.js';
3
- export type { JobRun, JobRunStatus, JobRunStore } from '@exellix/exellix-db';
4
- export { createMemoryJobRunStore, createJobRunStore } from '@exellix/exellix-db';
5
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,SAAS,EACT,MAAM,EACN,WAAW,EACX,QAAQ,EACR,KAAK,aAAa,EAClB,KAAK,OAAO,EACZ,KAAK,YAAY,GAClB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,eAAe,EAAE,KAAK,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAEhF,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAC7E,OAAO,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC"}
package/dist/index.js DELETED
@@ -1,4 +0,0 @@
1
- export { countByStatus, countRuns, getRun, groupByItem, listRuns, } from './queries.js';
2
- export { startJobsUiHttp } from './http/server.js';
3
- export { createMemoryJobRunStore, createJobRunStore } from '@exellix/exellix-db';
4
- //# sourceMappingURL=index.js.map
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,SAAS,EACT,MAAM,EACN,WAAW,EACX,QAAQ,GAIT,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,eAAe,EAA+B,MAAM,kBAAkB,CAAC;AAGhF,OAAO,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC"}
package/dist/queries.d.ts DELETED
@@ -1,26 +0,0 @@
1
- import type { JobRun, JobRunStatus, JobRunStore } from '@exellix/exellix-db';
2
- export type ListRunsQuery = {
3
- jobDefId?: string;
4
- status?: JobRunStatus | JobRunStatus[];
5
- itemId?: string;
6
- graphId?: string;
7
- page?: number;
8
- pageSize?: number;
9
- };
10
- export type RunPage = {
11
- runs: JobRun[];
12
- total: number;
13
- page: number;
14
- pageSize: number;
15
- };
16
- export type StatusCounts = Record<JobRunStatus, number>;
17
- export declare function listRuns(store: JobRunStore, query?: ListRunsQuery): Promise<RunPage>;
18
- export declare function getRun(store: JobRunStore, id: string): Promise<JobRun | null>;
19
- export declare function groupByItem(store: JobRunStore, itemId: string): Promise<JobRun[]>;
20
- export declare function countByStatus(store: JobRunStore, jobDefId?: string): Promise<StatusCounts>;
21
- export declare function countRuns(store: JobRunStore, filter: {
22
- jobDefId?: string;
23
- graphId?: string;
24
- status?: JobRunStatus | JobRunStatus[];
25
- }): Promise<number>;
26
- //# sourceMappingURL=queries.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"queries.d.ts","sourceRoot":"","sources":["../src/queries.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAE7E,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,YAAY,GAAG,YAAY,EAAE,CAAC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG;IACpB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;AAIxD,wBAAsB,QAAQ,CAAC,KAAK,EAAE,WAAW,EAAE,KAAK,GAAE,aAAkB,GAAG,OAAO,CAAC,OAAO,CAAC,CAgB9F;AAED,wBAAsB,MAAM,CAAC,KAAK,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAEnF;AAED,wBAAsB,WAAW,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAEvF;AAED,wBAAsB,aAAa,CAAC,KAAK,EAAE,WAAW,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAMhG;AAED,wBAAsB,SAAS,CAC7B,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,YAAY,GAAG,YAAY,EAAE,CAAA;CAAE,GACtF,OAAO,CAAC,MAAM,CAAC,CAEjB"}
package/dist/queries.js DELETED
@@ -1,33 +0,0 @@
1
- const ALL_STATUSES = ['blocked', 'pending', 'running', 'done', 'failed'];
2
- export async function listRuns(store, query = {}) {
3
- const page = Math.max(1, query.page ?? 1);
4
- const pageSize = Math.min(500, Math.max(1, query.pageSize ?? 50));
5
- const skip = (page - 1) * pageSize;
6
- const filter = {
7
- jobDefId: query.jobDefId,
8
- status: query.status,
9
- itemId: query.itemId,
10
- graphId: query.graphId,
11
- sort: { createdAt: -1 },
12
- };
13
- const total = await store.count(filter);
14
- const runs = await store.find({ ...filter, skip, limit: pageSize });
15
- return { runs, total, page, pageSize };
16
- }
17
- export async function getRun(store, id) {
18
- return store.get(id);
19
- }
20
- export async function groupByItem(store, itemId) {
21
- return store.find({ itemId, sort: { createdAt: 1 } });
22
- }
23
- export async function countByStatus(store, jobDefId) {
24
- const counts = Object.fromEntries(ALL_STATUSES.map((s) => [s, 0]));
25
- for (const status of ALL_STATUSES) {
26
- counts[status] = await store.count({ jobDefId, status });
27
- }
28
- return counts;
29
- }
30
- export async function countRuns(store, filter) {
31
- return store.count(filter);
32
- }
33
- //# sourceMappingURL=queries.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"queries.js","sourceRoot":"","sources":["../src/queries.ts"],"names":[],"mappings":"AAoBA,MAAM,YAAY,GAAmB,CAAC,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;AAEzF,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,KAAkB,EAAE,QAAuB,EAAE;IAC1E,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC;IAC1C,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,CAAC;IAClE,MAAM,IAAI,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC;IAEnC,MAAM,MAAM,GAAG;QACb,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,IAAI,EAAE,EAAE,SAAS,EAAE,CAAC,CAAU,EAAE;KACjC,CAAC;IAEF,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACxC,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,EAAE,GAAG,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;IACpE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;AACzC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,KAAkB,EAAE,EAAU;IACzD,OAAO,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AACvB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,KAAkB,EAAE,MAAc;IAClE,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;AACxD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,KAAkB,EAAE,QAAiB;IACvE,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAiB,CAAC;IACnF,KAAK,MAAM,MAAM,IAAI,YAAY,EAAE,CAAC;QAClC,MAAM,CAAC,MAAM,CAAC,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;IAC3D,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,KAAkB,EAClB,MAAuF;IAEvF,OAAO,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;AAC7B,CAAC"}