flowscale 2.1.0-beta.1 → 2.1.0-beta.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/dist/index.d.ts +1 -0
- package/dist/index.js +3 -1
- package/dist/local-types.d.ts +6 -0
- package/dist/node-index.d.ts +2 -0
- package/dist/node-index.js +7 -0
- package/dist/node-transport.d.ts +80 -0
- package/dist/node-transport.js +400 -0
- package/dist/operator-server.d.ts +26 -0
- package/dist/operator-server.js +178 -0
- package/dist/remote-transport.d.ts +43 -0
- package/dist/remote-transport.js +175 -0
- package/package.json +20 -1
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -73,7 +73,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
73
73
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
74
74
|
};
|
|
75
75
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
76
|
-
exports.LocalPodsAPI = exports.FlowscalePlatformAPI = exports.FlowscaleAPI = void 0;
|
|
76
|
+
exports.RemotePodsTransport = exports.LocalPodsAPI = exports.FlowscalePlatformAPI = exports.FlowscaleAPI = void 0;
|
|
77
77
|
var axios_1 = __importDefault(require("axios"));
|
|
78
78
|
var FlowscaleAPI = /** @class */ (function () {
|
|
79
79
|
function FlowscaleAPI(config) {
|
|
@@ -1302,3 +1302,5 @@ var local_pods_1 = require("./local-pods");
|
|
|
1302
1302
|
Object.defineProperty(exports, "LocalPodsAPI", { enumerable: true, get: function () { return local_pods_1.LocalPodsAPI; } });
|
|
1303
1303
|
__exportStar(require("./local-types"), exports);
|
|
1304
1304
|
__exportStar(require("./platform-types"), exports);
|
|
1305
|
+
var remote_transport_1 = require("./remote-transport");
|
|
1306
|
+
Object.defineProperty(exports, "RemotePodsTransport", { enumerable: true, get: function () { return remote_transport_1.RemotePodsTransport; } });
|
package/dist/local-types.d.ts
CHANGED
|
@@ -33,6 +33,12 @@ export interface LocalPodInstance {
|
|
|
33
33
|
lastDetectedAt: number | null;
|
|
34
34
|
comfyuiInfo?: ComfyUISystemStats;
|
|
35
35
|
createdAt: number;
|
|
36
|
+
/**
|
|
37
|
+
* Pre-computed base URL for reaching this instance.
|
|
38
|
+
* Set by RemotePodsTransport to `http://operatorHost:port`.
|
|
39
|
+
* When undefined, consumers should fall back to `http://localhost:port`.
|
|
40
|
+
*/
|
|
41
|
+
baseUrl?: string;
|
|
36
42
|
}
|
|
37
43
|
export interface LocalPod {
|
|
38
44
|
id: string;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PodsOperatorServer = exports.NodePodsTransport = void 0;
|
|
4
|
+
var node_transport_1 = require("./node-transport");
|
|
5
|
+
Object.defineProperty(exports, "NodePodsTransport", { enumerable: true, get: function () { return node_transport_1.NodePodsTransport; } });
|
|
6
|
+
var operator_server_1 = require("./operator-server");
|
|
7
|
+
Object.defineProperty(exports, "PodsOperatorServer", { enumerable: true, get: function () { return operator_server_1.PodsOperatorServer; } });
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { LocalPodsTransport, LocalPod, LocalPodInstance, LocalPodDevice, LocalPodInstanceStatus, SystemInfo, ComfyUISystemStats, CreateLocalPodRequest, UpdateLocalPodRequest, StartInstanceRequest } from './local-types';
|
|
2
|
+
/**
|
|
3
|
+
* NodePodsTransport — manages local pod data in a shared SQLite DB at
|
|
4
|
+
* `~/.flowscale/pods.db` (or a custom path).
|
|
5
|
+
*
|
|
6
|
+
* Implements CRUD for pods and instances. Process-lifecycle methods
|
|
7
|
+
* (startInstance, stopInstance, etc.) are NOT implemented here; they
|
|
8
|
+
* must be provided by the consuming application (e.g. creativeflow-electron).
|
|
9
|
+
*
|
|
10
|
+
* Export path: `flowscale/node`
|
|
11
|
+
*/
|
|
12
|
+
export declare class NodePodsTransport implements LocalPodsTransport {
|
|
13
|
+
/** The underlying better-sqlite3 database handle */
|
|
14
|
+
private db;
|
|
15
|
+
private emitter;
|
|
16
|
+
constructor(dbPath?: string);
|
|
17
|
+
private _initSchema;
|
|
18
|
+
create(data: CreateLocalPodRequest): Promise<LocalPod>;
|
|
19
|
+
list(): Promise<LocalPod[]>;
|
|
20
|
+
get(id: string): Promise<LocalPod | null>;
|
|
21
|
+
update(id: string, data: UpdateLocalPodRequest): Promise<LocalPod>;
|
|
22
|
+
delete(id: string): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Insert a new instance row and return the hydrated LocalPodInstance.
|
|
25
|
+
* Used by process-management code (e.g. startInstance in electron).
|
|
26
|
+
*/
|
|
27
|
+
dbInsertInstance(data: {
|
|
28
|
+
id: string;
|
|
29
|
+
podId: string;
|
|
30
|
+
port: number;
|
|
31
|
+
device: LocalPodDevice;
|
|
32
|
+
status: LocalPodInstanceStatus;
|
|
33
|
+
pid: number | null;
|
|
34
|
+
lastDetectedAt: number | null;
|
|
35
|
+
createdAt: number;
|
|
36
|
+
}): LocalPodInstance;
|
|
37
|
+
/**
|
|
38
|
+
* Update specific fields of an instance row.
|
|
39
|
+
* Used by process-management code for status/pid updates.
|
|
40
|
+
*/
|
|
41
|
+
dbUpdateInstance(id: string, updates: {
|
|
42
|
+
status?: LocalPodInstanceStatus;
|
|
43
|
+
pid?: number | null;
|
|
44
|
+
lastDetectedAt?: number | null;
|
|
45
|
+
device?: LocalPodDevice;
|
|
46
|
+
}): void;
|
|
47
|
+
/** Get a single instance row by ID. */
|
|
48
|
+
dbGetInstance(id: string): LocalPodInstance | null;
|
|
49
|
+
/** Get all instances for a given pod. */
|
|
50
|
+
dbGetInstancesForPod(podId: string): LocalPodInstance[];
|
|
51
|
+
/** Get all instance rows across all pods. */
|
|
52
|
+
dbGetAllInstances(): LocalPodInstance[];
|
|
53
|
+
/** Get a raw pod row (for accessing comfyuiPath etc in process-mgmt code). */
|
|
54
|
+
dbGetPodRow(id: string): {
|
|
55
|
+
id: string;
|
|
56
|
+
comfyuiPath: string | null;
|
|
57
|
+
} | null;
|
|
58
|
+
deleteInstance(instanceId: string): Promise<void>;
|
|
59
|
+
refreshInstance(instanceId: string): Promise<LocalPodInstance>;
|
|
60
|
+
onInstanceStatusChanged(cb: (instanceId: string, status: LocalPodInstanceStatus, info?: ComfyUISystemStats) => void): () => void;
|
|
61
|
+
onLog(cb: (instanceId: string, line: string) => void): () => void;
|
|
62
|
+
/** Emit an instanceStatusChanged event (called by subclasses / process-lifecycle code). */
|
|
63
|
+
emitInstanceStatusChanged(instanceId: string, status: LocalPodInstanceStatus, info?: ComfyUISystemStats): void;
|
|
64
|
+
/** Emit a log event (called by subclasses / process-lifecycle code). */
|
|
65
|
+
emitLog(instanceId: string, line: string): void;
|
|
66
|
+
scanPaths(): Promise<string | null>;
|
|
67
|
+
scanPorts(_podId: string): Promise<LocalPodInstance[]>;
|
|
68
|
+
startInstance(_req: StartInstanceRequest): Promise<LocalPodInstance>;
|
|
69
|
+
stopInstance(_instanceId: string): Promise<void>;
|
|
70
|
+
restartInstance(_instanceId: string): Promise<void>;
|
|
71
|
+
getSystemInfo(): Promise<SystemInfo>;
|
|
72
|
+
getLogs(_instanceId: string): Promise<string[]>;
|
|
73
|
+
installRocmPyTorch(_podId: string): void;
|
|
74
|
+
onRocmSetupDone(_cb: (data: {
|
|
75
|
+
podId: string;
|
|
76
|
+
success: boolean;
|
|
77
|
+
}) => void): () => void;
|
|
78
|
+
private _getInstancesForPod;
|
|
79
|
+
private _probePort;
|
|
80
|
+
}
|
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
36
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
37
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
38
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
39
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
40
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
41
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
45
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
46
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
47
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
48
|
+
function step(op) {
|
|
49
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
50
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
51
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
52
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
53
|
+
switch (op[0]) {
|
|
54
|
+
case 0: case 1: t = op; break;
|
|
55
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
56
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
57
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
58
|
+
default:
|
|
59
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
60
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
61
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
62
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
63
|
+
if (t[2]) _.ops.pop();
|
|
64
|
+
_.trys.pop(); continue;
|
|
65
|
+
}
|
|
66
|
+
op = body.call(thisArg, _);
|
|
67
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
68
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
72
|
+
exports.NodePodsTransport = void 0;
|
|
73
|
+
var path = __importStar(require("path"));
|
|
74
|
+
var os = __importStar(require("os"));
|
|
75
|
+
var fs = __importStar(require("fs"));
|
|
76
|
+
var crypto_1 = require("crypto");
|
|
77
|
+
var events_1 = require("events");
|
|
78
|
+
// ── Helpers ────────────────────────────────────────────────────────────────────
|
|
79
|
+
function rowToInstance(row) {
|
|
80
|
+
var _a, _b;
|
|
81
|
+
return {
|
|
82
|
+
id: row.id,
|
|
83
|
+
podId: row.pod_id,
|
|
84
|
+
port: row.port,
|
|
85
|
+
device: JSON.parse(row.device),
|
|
86
|
+
status: row.status,
|
|
87
|
+
pid: (_a = row.pid) !== null && _a !== void 0 ? _a : null,
|
|
88
|
+
lastDetectedAt: (_b = row.last_detected_at) !== null && _b !== void 0 ? _b : null,
|
|
89
|
+
createdAt: row.created_at,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function rowToPod(row, instances) {
|
|
93
|
+
var _a, _b;
|
|
94
|
+
return {
|
|
95
|
+
id: row.id,
|
|
96
|
+
name: row.name,
|
|
97
|
+
description: (_a = row.description) !== null && _a !== void 0 ? _a : undefined,
|
|
98
|
+
comfyuiPath: (_b = row.comfyui_path) !== null && _b !== void 0 ? _b : null,
|
|
99
|
+
instances: instances,
|
|
100
|
+
createdAt: row.created_at,
|
|
101
|
+
updatedAt: row.updated_at,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
// ── NodePodsTransport ──────────────────────────────────────────────────────────
|
|
105
|
+
/**
|
|
106
|
+
* NodePodsTransport — manages local pod data in a shared SQLite DB at
|
|
107
|
+
* `~/.flowscale/pods.db` (or a custom path).
|
|
108
|
+
*
|
|
109
|
+
* Implements CRUD for pods and instances. Process-lifecycle methods
|
|
110
|
+
* (startInstance, stopInstance, etc.) are NOT implemented here; they
|
|
111
|
+
* must be provided by the consuming application (e.g. creativeflow-electron).
|
|
112
|
+
*
|
|
113
|
+
* Export path: `flowscale/node`
|
|
114
|
+
*/
|
|
115
|
+
var NodePodsTransport = /** @class */ (function () {
|
|
116
|
+
function NodePodsTransport(dbPath) {
|
|
117
|
+
this.emitter = new events_1.EventEmitter();
|
|
118
|
+
var resolvedPath = dbPath !== null && dbPath !== void 0 ? dbPath : path.join(os.homedir(), '.flowscale', 'pods.db');
|
|
119
|
+
// Ensure parent directory exists
|
|
120
|
+
fs.mkdirSync(path.dirname(resolvedPath), { recursive: true });
|
|
121
|
+
// Lazy-require so consumers that don't use this class don't need better-sqlite3
|
|
122
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
123
|
+
var Database = require('better-sqlite3');
|
|
124
|
+
this.db = new Database(resolvedPath);
|
|
125
|
+
this.db.pragma('journal_mode = WAL');
|
|
126
|
+
this.db.pragma('foreign_keys = ON');
|
|
127
|
+
this._initSchema();
|
|
128
|
+
}
|
|
129
|
+
NodePodsTransport.prototype._initSchema = function () {
|
|
130
|
+
this.db.exec("\n CREATE TABLE IF NOT EXISTS pods (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n description TEXT,\n comfyui_path TEXT,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL\n );\n\n CREATE TABLE IF NOT EXISTS pod_instances (\n id TEXT PRIMARY KEY,\n pod_id TEXT NOT NULL REFERENCES pods(id) ON DELETE CASCADE,\n port INTEGER NOT NULL,\n device TEXT NOT NULL,\n status TEXT NOT NULL DEFAULT 'idle',\n pid INTEGER,\n last_detected_at INTEGER,\n created_at INTEGER NOT NULL\n );\n ");
|
|
131
|
+
};
|
|
132
|
+
// ── Pod CRUD ───────────────────────────────────────────────────────────────
|
|
133
|
+
NodePodsTransport.prototype.create = function (data) {
|
|
134
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
135
|
+
var id, now, row;
|
|
136
|
+
var _a, _b;
|
|
137
|
+
return __generator(this, function (_c) {
|
|
138
|
+
id = (0, crypto_1.randomUUID)();
|
|
139
|
+
now = Date.now();
|
|
140
|
+
this.db
|
|
141
|
+
.prepare('INSERT INTO pods (id, name, description, comfyui_path, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)')
|
|
142
|
+
.run(id, data.name, (_a = data.description) !== null && _a !== void 0 ? _a : null, (_b = data.comfyuiPath) !== null && _b !== void 0 ? _b : null, now, now);
|
|
143
|
+
row = this.db.prepare('SELECT * FROM pods WHERE id = ?').get(id);
|
|
144
|
+
return [2 /*return*/, rowToPod(row, [])];
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
};
|
|
148
|
+
NodePodsTransport.prototype.list = function () {
|
|
149
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
150
|
+
var rows;
|
|
151
|
+
var _this = this;
|
|
152
|
+
return __generator(this, function (_a) {
|
|
153
|
+
rows = this.db.prepare('SELECT * FROM pods ORDER BY created_at ASC').all();
|
|
154
|
+
return [2 /*return*/, rows.map(function (row) {
|
|
155
|
+
var instances = _this._getInstancesForPod(row.id);
|
|
156
|
+
return rowToPod(row, instances);
|
|
157
|
+
})];
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
};
|
|
161
|
+
NodePodsTransport.prototype.get = function (id) {
|
|
162
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
163
|
+
var row, instances;
|
|
164
|
+
return __generator(this, function (_a) {
|
|
165
|
+
row = this.db.prepare('SELECT * FROM pods WHERE id = ?').get(id);
|
|
166
|
+
if (!row)
|
|
167
|
+
return [2 /*return*/, null];
|
|
168
|
+
instances = this._getInstancesForPod(id);
|
|
169
|
+
return [2 /*return*/, rowToPod(row, instances)];
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
};
|
|
173
|
+
NodePodsTransport.prototype.update = function (id, data) {
|
|
174
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
175
|
+
var now, sets, values, row, instances;
|
|
176
|
+
var _a;
|
|
177
|
+
var _b, _c;
|
|
178
|
+
return __generator(this, function (_d) {
|
|
179
|
+
now = Date.now();
|
|
180
|
+
sets = ['updated_at = ?'];
|
|
181
|
+
values = [now];
|
|
182
|
+
if (data.name !== undefined) {
|
|
183
|
+
sets.push('name = ?');
|
|
184
|
+
values.push(data.name);
|
|
185
|
+
}
|
|
186
|
+
if (data.description !== undefined) {
|
|
187
|
+
sets.push('description = ?');
|
|
188
|
+
values.push((_b = data.description) !== null && _b !== void 0 ? _b : null);
|
|
189
|
+
}
|
|
190
|
+
if ('comfyuiPath' in data) {
|
|
191
|
+
sets.push('comfyui_path = ?');
|
|
192
|
+
values.push((_c = data.comfyuiPath) !== null && _c !== void 0 ? _c : null);
|
|
193
|
+
}
|
|
194
|
+
values.push(id);
|
|
195
|
+
(_a = this.db.prepare("UPDATE pods SET ".concat(sets.join(', '), " WHERE id = ?"))).run.apply(_a, values);
|
|
196
|
+
row = this.db.prepare('SELECT * FROM pods WHERE id = ?').get(id);
|
|
197
|
+
instances = this._getInstancesForPod(id);
|
|
198
|
+
return [2 /*return*/, rowToPod(row, instances)];
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
};
|
|
202
|
+
NodePodsTransport.prototype.delete = function (id) {
|
|
203
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
204
|
+
return __generator(this, function (_a) {
|
|
205
|
+
this.db.prepare('DELETE FROM pods WHERE id = ?').run(id);
|
|
206
|
+
return [2 /*return*/];
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
};
|
|
210
|
+
// ── Instance CRUD helpers (for use by process-lifecycle code) ──────────────
|
|
211
|
+
/**
|
|
212
|
+
* Insert a new instance row and return the hydrated LocalPodInstance.
|
|
213
|
+
* Used by process-management code (e.g. startInstance in electron).
|
|
214
|
+
*/
|
|
215
|
+
NodePodsTransport.prototype.dbInsertInstance = function (data) {
|
|
216
|
+
this.db
|
|
217
|
+
.prepare("INSERT INTO pod_instances (id, pod_id, port, device, status, pid, last_detected_at, created_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)")
|
|
218
|
+
.run(data.id, data.podId, data.port, JSON.stringify(data.device), data.status, data.pid, data.lastDetectedAt, data.createdAt);
|
|
219
|
+
var row = this.db
|
|
220
|
+
.prepare('SELECT * FROM pod_instances WHERE id = ?')
|
|
221
|
+
.get(data.id);
|
|
222
|
+
return rowToInstance(row);
|
|
223
|
+
};
|
|
224
|
+
/**
|
|
225
|
+
* Update specific fields of an instance row.
|
|
226
|
+
* Used by process-management code for status/pid updates.
|
|
227
|
+
*/
|
|
228
|
+
NodePodsTransport.prototype.dbUpdateInstance = function (id, updates) {
|
|
229
|
+
var _a;
|
|
230
|
+
var _b, _c;
|
|
231
|
+
var sets = [];
|
|
232
|
+
var values = [];
|
|
233
|
+
if (updates.status !== undefined) {
|
|
234
|
+
sets.push('status = ?');
|
|
235
|
+
values.push(updates.status);
|
|
236
|
+
}
|
|
237
|
+
if ('pid' in updates) {
|
|
238
|
+
sets.push('pid = ?');
|
|
239
|
+
values.push((_b = updates.pid) !== null && _b !== void 0 ? _b : null);
|
|
240
|
+
}
|
|
241
|
+
if ('lastDetectedAt' in updates) {
|
|
242
|
+
sets.push('last_detected_at = ?');
|
|
243
|
+
values.push((_c = updates.lastDetectedAt) !== null && _c !== void 0 ? _c : null);
|
|
244
|
+
}
|
|
245
|
+
if (updates.device !== undefined) {
|
|
246
|
+
sets.push('device = ?');
|
|
247
|
+
values.push(JSON.stringify(updates.device));
|
|
248
|
+
}
|
|
249
|
+
if (sets.length === 0)
|
|
250
|
+
return;
|
|
251
|
+
values.push(id);
|
|
252
|
+
(_a = this.db.prepare("UPDATE pod_instances SET ".concat(sets.join(', '), " WHERE id = ?"))).run.apply(_a, values);
|
|
253
|
+
};
|
|
254
|
+
/** Get a single instance row by ID. */
|
|
255
|
+
NodePodsTransport.prototype.dbGetInstance = function (id) {
|
|
256
|
+
var row = this.db
|
|
257
|
+
.prepare('SELECT * FROM pod_instances WHERE id = ?')
|
|
258
|
+
.get(id);
|
|
259
|
+
return row ? rowToInstance(row) : null;
|
|
260
|
+
};
|
|
261
|
+
/** Get all instances for a given pod. */
|
|
262
|
+
NodePodsTransport.prototype.dbGetInstancesForPod = function (podId) {
|
|
263
|
+
return this._getInstancesForPod(podId);
|
|
264
|
+
};
|
|
265
|
+
/** Get all instance rows across all pods. */
|
|
266
|
+
NodePodsTransport.prototype.dbGetAllInstances = function () {
|
|
267
|
+
var rows = this.db
|
|
268
|
+
.prepare('SELECT * FROM pod_instances')
|
|
269
|
+
.all();
|
|
270
|
+
return rows.map(rowToInstance);
|
|
271
|
+
};
|
|
272
|
+
/** Get a raw pod row (for accessing comfyuiPath etc in process-mgmt code). */
|
|
273
|
+
NodePodsTransport.prototype.dbGetPodRow = function (id) {
|
|
274
|
+
var _a;
|
|
275
|
+
var row = this.db
|
|
276
|
+
.prepare('SELECT * FROM pods WHERE id = ?')
|
|
277
|
+
.get(id);
|
|
278
|
+
if (!row)
|
|
279
|
+
return null;
|
|
280
|
+
return { id: row.id, comfyuiPath: (_a = row.comfyui_path) !== null && _a !== void 0 ? _a : null };
|
|
281
|
+
};
|
|
282
|
+
// ── Instance interface methods ─────────────────────────────────────────────
|
|
283
|
+
NodePodsTransport.prototype.deleteInstance = function (instanceId) {
|
|
284
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
285
|
+
return __generator(this, function (_a) {
|
|
286
|
+
this.db.prepare('DELETE FROM pod_instances WHERE id = ?').run(instanceId);
|
|
287
|
+
return [2 /*return*/];
|
|
288
|
+
});
|
|
289
|
+
});
|
|
290
|
+
};
|
|
291
|
+
NodePodsTransport.prototype.refreshInstance = function (instanceId) {
|
|
292
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
293
|
+
var row, info, now, newStatus, updated, inst;
|
|
294
|
+
return __generator(this, function (_a) {
|
|
295
|
+
switch (_a.label) {
|
|
296
|
+
case 0:
|
|
297
|
+
row = this.db
|
|
298
|
+
.prepare('SELECT * FROM pod_instances WHERE id = ?')
|
|
299
|
+
.get(instanceId);
|
|
300
|
+
if (!row)
|
|
301
|
+
throw new Error("Instance ".concat(instanceId, " not found"));
|
|
302
|
+
return [4 /*yield*/, this._probePort(row.port)];
|
|
303
|
+
case 1:
|
|
304
|
+
info = _a.sent();
|
|
305
|
+
now = Date.now();
|
|
306
|
+
newStatus = info ? 'running' : 'idle';
|
|
307
|
+
this.db
|
|
308
|
+
.prepare('UPDATE pod_instances SET status = ?, last_detected_at = ? WHERE id = ?')
|
|
309
|
+
.run(newStatus, info ? now : row.last_detected_at, instanceId);
|
|
310
|
+
updated = this.db
|
|
311
|
+
.prepare('SELECT * FROM pod_instances WHERE id = ?')
|
|
312
|
+
.get(instanceId);
|
|
313
|
+
inst = rowToInstance(updated);
|
|
314
|
+
this.emitter.emit('instanceStatusChanged', instanceId, newStatus, info !== null && info !== void 0 ? info : undefined);
|
|
315
|
+
return [2 /*return*/, inst];
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
};
|
|
320
|
+
// ── Events ─────────────────────────────────────────────────────────────────
|
|
321
|
+
NodePodsTransport.prototype.onInstanceStatusChanged = function (cb) {
|
|
322
|
+
var _this = this;
|
|
323
|
+
this.emitter.on('instanceStatusChanged', cb);
|
|
324
|
+
return function () { return _this.emitter.off('instanceStatusChanged', cb); };
|
|
325
|
+
};
|
|
326
|
+
NodePodsTransport.prototype.onLog = function (cb) {
|
|
327
|
+
var _this = this;
|
|
328
|
+
this.emitter.on('log', cb);
|
|
329
|
+
return function () { return _this.emitter.off('log', cb); };
|
|
330
|
+
};
|
|
331
|
+
/** Emit an instanceStatusChanged event (called by subclasses / process-lifecycle code). */
|
|
332
|
+
NodePodsTransport.prototype.emitInstanceStatusChanged = function (instanceId, status, info) {
|
|
333
|
+
this.emitter.emit('instanceStatusChanged', instanceId, status, info);
|
|
334
|
+
};
|
|
335
|
+
/** Emit a log event (called by subclasses / process-lifecycle code). */
|
|
336
|
+
NodePodsTransport.prototype.emitLog = function (instanceId, line) {
|
|
337
|
+
this.emitter.emit('log', instanceId, line);
|
|
338
|
+
};
|
|
339
|
+
// ── Not-implemented stubs ──────────────────────────────────────────────────
|
|
340
|
+
NodePodsTransport.prototype.scanPaths = function () {
|
|
341
|
+
return Promise.reject(new Error('NodePodsTransport: scanPaths() not implemented — use full LocalPodService'));
|
|
342
|
+
};
|
|
343
|
+
NodePodsTransport.prototype.scanPorts = function (_podId) {
|
|
344
|
+
return Promise.reject(new Error('NodePodsTransport: scanPorts() not implemented — use full LocalPodService'));
|
|
345
|
+
};
|
|
346
|
+
NodePodsTransport.prototype.startInstance = function (_req) {
|
|
347
|
+
return Promise.reject(new Error('NodePodsTransport: startInstance() not implemented — use full LocalPodService'));
|
|
348
|
+
};
|
|
349
|
+
NodePodsTransport.prototype.stopInstance = function (_instanceId) {
|
|
350
|
+
return Promise.reject(new Error('NodePodsTransport: stopInstance() not implemented — use full LocalPodService'));
|
|
351
|
+
};
|
|
352
|
+
NodePodsTransport.prototype.restartInstance = function (_instanceId) {
|
|
353
|
+
return Promise.reject(new Error('NodePodsTransport: restartInstance() not implemented — use full LocalPodService'));
|
|
354
|
+
};
|
|
355
|
+
NodePodsTransport.prototype.getSystemInfo = function () {
|
|
356
|
+
return Promise.reject(new Error('NodePodsTransport: getSystemInfo() not implemented — use full LocalPodService'));
|
|
357
|
+
};
|
|
358
|
+
NodePodsTransport.prototype.getLogs = function (_instanceId) {
|
|
359
|
+
return Promise.reject(new Error('NodePodsTransport: getLogs() not implemented — use full LocalPodService'));
|
|
360
|
+
};
|
|
361
|
+
NodePodsTransport.prototype.installRocmPyTorch = function (_podId) {
|
|
362
|
+
throw new Error('NodePodsTransport: installRocmPyTorch() not implemented — use full LocalPodService');
|
|
363
|
+
};
|
|
364
|
+
NodePodsTransport.prototype.onRocmSetupDone = function (_cb) {
|
|
365
|
+
throw new Error('NodePodsTransport: onRocmSetupDone() not implemented — use full LocalPodService');
|
|
366
|
+
};
|
|
367
|
+
// ── Private helpers ────────────────────────────────────────────────────────
|
|
368
|
+
NodePodsTransport.prototype._getInstancesForPod = function (podId) {
|
|
369
|
+
var rows = this.db
|
|
370
|
+
.prepare('SELECT * FROM pod_instances WHERE pod_id = ?')
|
|
371
|
+
.all(podId);
|
|
372
|
+
return rows.map(rowToInstance);
|
|
373
|
+
};
|
|
374
|
+
NodePodsTransport.prototype._probePort = function (port) {
|
|
375
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
376
|
+
var res, _a;
|
|
377
|
+
return __generator(this, function (_b) {
|
|
378
|
+
switch (_b.label) {
|
|
379
|
+
case 0:
|
|
380
|
+
_b.trys.push([0, 3, , 4]);
|
|
381
|
+
return [4 /*yield*/, fetch("http://localhost:".concat(port, "/system_stats"), {
|
|
382
|
+
signal: AbortSignal.timeout(1500)
|
|
383
|
+
})];
|
|
384
|
+
case 1:
|
|
385
|
+
res = _b.sent();
|
|
386
|
+
if (!res.ok)
|
|
387
|
+
return [2 /*return*/, null];
|
|
388
|
+
return [4 /*yield*/, res.json()];
|
|
389
|
+
case 2: return [2 /*return*/, (_b.sent())];
|
|
390
|
+
case 3:
|
|
391
|
+
_a = _b.sent();
|
|
392
|
+
return [2 /*return*/, null];
|
|
393
|
+
case 4: return [2 /*return*/];
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
});
|
|
397
|
+
};
|
|
398
|
+
return NodePodsTransport;
|
|
399
|
+
}());
|
|
400
|
+
exports.NodePodsTransport = NodePodsTransport;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { NodePodsTransport } from './node-transport';
|
|
2
|
+
/**
|
|
3
|
+
* PodsOperatorServer — exposes a read-only pod list HTTP API so that remote
|
|
4
|
+
* FlowScale Studio instances on the same network can discover and use pods
|
|
5
|
+
* managed by this machine.
|
|
6
|
+
*
|
|
7
|
+
* Serves on all interfaces (0.0.0.0) so any machine on the LAN can reach it.
|
|
8
|
+
* Includes CORS headers for browser-based clients.
|
|
9
|
+
*
|
|
10
|
+
* Usage in creativeflow-electron (main process):
|
|
11
|
+
* import { NodePodsTransport, PodsOperatorServer } from 'flowscale/node'
|
|
12
|
+
* const server = new PodsOperatorServer(transport) // default port 3001
|
|
13
|
+
* server.start()
|
|
14
|
+
*
|
|
15
|
+
* Remote clients connect via:
|
|
16
|
+
* import { RemotePodsTransport, LocalPodsAPI } from 'flowscale'
|
|
17
|
+
* const api = new LocalPodsAPI(new RemotePodsTransport('http://192.168.1.100:3001'))
|
|
18
|
+
*/
|
|
19
|
+
export declare class PodsOperatorServer {
|
|
20
|
+
private server;
|
|
21
|
+
private readonly transport;
|
|
22
|
+
private readonly port;
|
|
23
|
+
constructor(transport: NodePodsTransport, port?: number);
|
|
24
|
+
start(): void;
|
|
25
|
+
stop(): void;
|
|
26
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
36
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
37
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
38
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
39
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
40
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
41
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
45
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
46
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
47
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
48
|
+
function step(op) {
|
|
49
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
50
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
51
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
52
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
53
|
+
switch (op[0]) {
|
|
54
|
+
case 0: case 1: t = op; break;
|
|
55
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
56
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
57
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
58
|
+
default:
|
|
59
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
60
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
61
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
62
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
63
|
+
if (t[2]) _.ops.pop();
|
|
64
|
+
_.trys.pop(); continue;
|
|
65
|
+
}
|
|
66
|
+
op = body.call(thisArg, _);
|
|
67
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
68
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
72
|
+
exports.PodsOperatorServer = void 0;
|
|
73
|
+
var http = __importStar(require("http"));
|
|
74
|
+
/**
|
|
75
|
+
* PodsOperatorServer — exposes a read-only pod list HTTP API so that remote
|
|
76
|
+
* FlowScale Studio instances on the same network can discover and use pods
|
|
77
|
+
* managed by this machine.
|
|
78
|
+
*
|
|
79
|
+
* Serves on all interfaces (0.0.0.0) so any machine on the LAN can reach it.
|
|
80
|
+
* Includes CORS headers for browser-based clients.
|
|
81
|
+
*
|
|
82
|
+
* Usage in creativeflow-electron (main process):
|
|
83
|
+
* import { NodePodsTransport, PodsOperatorServer } from 'flowscale/node'
|
|
84
|
+
* const server = new PodsOperatorServer(transport) // default port 3001
|
|
85
|
+
* server.start()
|
|
86
|
+
*
|
|
87
|
+
* Remote clients connect via:
|
|
88
|
+
* import { RemotePodsTransport, LocalPodsAPI } from 'flowscale'
|
|
89
|
+
* const api = new LocalPodsAPI(new RemotePodsTransport('http://192.168.1.100:3001'))
|
|
90
|
+
*/
|
|
91
|
+
var PodsOperatorServer = /** @class */ (function () {
|
|
92
|
+
function PodsOperatorServer(transport, port) {
|
|
93
|
+
if (port === void 0) { port = 3001; }
|
|
94
|
+
this.server = null;
|
|
95
|
+
this.transport = transport;
|
|
96
|
+
this.port = port;
|
|
97
|
+
}
|
|
98
|
+
PodsOperatorServer.prototype.start = function () {
|
|
99
|
+
var _this = this;
|
|
100
|
+
if (this.server)
|
|
101
|
+
return;
|
|
102
|
+
this.server = http.createServer(function (req, res) { return __awaiter(_this, void 0, void 0, function () {
|
|
103
|
+
var url, pods, podMatch, id, pod, err_1;
|
|
104
|
+
var _a;
|
|
105
|
+
return __generator(this, function (_b) {
|
|
106
|
+
switch (_b.label) {
|
|
107
|
+
case 0:
|
|
108
|
+
// CORS — allow any origin (LAN usage only, not a public service)
|
|
109
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
110
|
+
res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');
|
|
111
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
112
|
+
if (req.method === 'OPTIONS') {
|
|
113
|
+
res.writeHead(204);
|
|
114
|
+
res.end();
|
|
115
|
+
return [2 /*return*/];
|
|
116
|
+
}
|
|
117
|
+
if (req.method !== 'GET') {
|
|
118
|
+
res.writeHead(405);
|
|
119
|
+
res.end('Method Not Allowed');
|
|
120
|
+
return [2 /*return*/];
|
|
121
|
+
}
|
|
122
|
+
url = ((_a = req.url) !== null && _a !== void 0 ? _a : '/').split('?')[0];
|
|
123
|
+
_b.label = 1;
|
|
124
|
+
case 1:
|
|
125
|
+
_b.trys.push([1, 6, , 7]);
|
|
126
|
+
if (!(url === '/api/pods' || url === '/api/pods/')) return [3 /*break*/, 3];
|
|
127
|
+
return [4 /*yield*/, this.transport.list()];
|
|
128
|
+
case 2:
|
|
129
|
+
pods = _b.sent();
|
|
130
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
131
|
+
res.end(JSON.stringify(pods));
|
|
132
|
+
return [2 /*return*/];
|
|
133
|
+
case 3:
|
|
134
|
+
podMatch = url.match(/^\/api\/pods\/([^/]+)$/);
|
|
135
|
+
if (!podMatch) return [3 /*break*/, 5];
|
|
136
|
+
id = decodeURIComponent(podMatch[1]);
|
|
137
|
+
return [4 /*yield*/, this.transport.get(id)];
|
|
138
|
+
case 4:
|
|
139
|
+
pod = _b.sent();
|
|
140
|
+
if (!pod) {
|
|
141
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
142
|
+
res.end(JSON.stringify({ error: 'Pod not found' }));
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
146
|
+
res.end(JSON.stringify(pod));
|
|
147
|
+
}
|
|
148
|
+
return [2 /*return*/];
|
|
149
|
+
case 5:
|
|
150
|
+
res.writeHead(404);
|
|
151
|
+
res.end('Not found');
|
|
152
|
+
return [3 /*break*/, 7];
|
|
153
|
+
case 6:
|
|
154
|
+
err_1 = _b.sent();
|
|
155
|
+
console.error('[PodsOperatorServer] Error handling request:', err_1);
|
|
156
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
157
|
+
res.end(JSON.stringify({ error: 'Internal server error' }));
|
|
158
|
+
return [3 /*break*/, 7];
|
|
159
|
+
case 7: return [2 /*return*/];
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
}); });
|
|
163
|
+
this.server.listen(this.port, '0.0.0.0', function () {
|
|
164
|
+
console.log("[PodsOperatorServer] Listening on 0.0.0.0:".concat(_this.port));
|
|
165
|
+
});
|
|
166
|
+
this.server.on('error', function (err) {
|
|
167
|
+
console.error('[PodsOperatorServer] Server error:', err);
|
|
168
|
+
});
|
|
169
|
+
};
|
|
170
|
+
PodsOperatorServer.prototype.stop = function () {
|
|
171
|
+
if (this.server) {
|
|
172
|
+
this.server.close();
|
|
173
|
+
this.server = null;
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
return PodsOperatorServer;
|
|
177
|
+
}());
|
|
178
|
+
exports.PodsOperatorServer = PodsOperatorServer;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { LocalPodsTransport, LocalPod, LocalPodInstance, LocalPodInstanceStatus, SystemInfo, ComfyUISystemStats, CreateLocalPodRequest, UpdateLocalPodRequest, StartInstanceRequest } from './local-types';
|
|
2
|
+
/**
|
|
3
|
+
* RemotePodsTransport — reads pods from a FlowScale Operator HTTP API
|
|
4
|
+
* served by PodsOperatorServer running on another machine.
|
|
5
|
+
*
|
|
6
|
+
* Only `list()` and `get()` are functional. All other methods throw since
|
|
7
|
+
* process lifecycle management must be done on the machine running the operator.
|
|
8
|
+
*
|
|
9
|
+
* Instance `baseUrl` fields are automatically set to `http://operatorHost:port`
|
|
10
|
+
* so that callers can reach the ComfyUI instances across the network.
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* import { RemotePodsTransport, LocalPodsAPI } from 'flowscale'
|
|
14
|
+
* const podsApi = new LocalPodsAPI(new RemotePodsTransport('http://192.168.1.100:3001'))
|
|
15
|
+
*/
|
|
16
|
+
export declare class RemotePodsTransport implements LocalPodsTransport {
|
|
17
|
+
private readonly operatorUrl;
|
|
18
|
+
private readonly operatorHost;
|
|
19
|
+
constructor(operatorUrl: string);
|
|
20
|
+
private _injectBaseUrls;
|
|
21
|
+
list(): Promise<LocalPod[]>;
|
|
22
|
+
get(id: string): Promise<LocalPod | null>;
|
|
23
|
+
private _notSupported;
|
|
24
|
+
create(_data: CreateLocalPodRequest): Promise<LocalPod>;
|
|
25
|
+
update(_id: string, _data: UpdateLocalPodRequest): Promise<LocalPod>;
|
|
26
|
+
delete(_id: string): Promise<void>;
|
|
27
|
+
scanPaths(): Promise<string | null>;
|
|
28
|
+
scanPorts(_podId: string): Promise<LocalPodInstance[]>;
|
|
29
|
+
refreshInstance(_instanceId: string): Promise<LocalPodInstance>;
|
|
30
|
+
startInstance(_req: StartInstanceRequest): Promise<LocalPodInstance>;
|
|
31
|
+
stopInstance(_instanceId: string): Promise<void>;
|
|
32
|
+
restartInstance(_instanceId: string): Promise<void>;
|
|
33
|
+
deleteInstance(_instanceId: string): Promise<void>;
|
|
34
|
+
getSystemInfo(): Promise<SystemInfo>;
|
|
35
|
+
getLogs(_instanceId: string): Promise<string[]>;
|
|
36
|
+
installRocmPyTorch(_podId: string): void;
|
|
37
|
+
onInstanceStatusChanged(_cb: (instanceId: string, status: LocalPodInstanceStatus, info?: ComfyUISystemStats) => void): () => void;
|
|
38
|
+
onLog(_cb: (instanceId: string, line: string) => void): () => void;
|
|
39
|
+
onRocmSetupDone(_cb: (data: {
|
|
40
|
+
podId: string;
|
|
41
|
+
success: boolean;
|
|
42
|
+
}) => void): () => void;
|
|
43
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __assign = (this && this.__assign) || function () {
|
|
3
|
+
__assign = Object.assign || function(t) {
|
|
4
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
+
s = arguments[i];
|
|
6
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
+
t[p] = s[p];
|
|
8
|
+
}
|
|
9
|
+
return t;
|
|
10
|
+
};
|
|
11
|
+
return __assign.apply(this, arguments);
|
|
12
|
+
};
|
|
13
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
14
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
15
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
16
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
17
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
18
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
19
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
23
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
24
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
25
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
26
|
+
function step(op) {
|
|
27
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
28
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
29
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
30
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
31
|
+
switch (op[0]) {
|
|
32
|
+
case 0: case 1: t = op; break;
|
|
33
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
34
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
35
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
36
|
+
default:
|
|
37
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
38
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
39
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
40
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
41
|
+
if (t[2]) _.ops.pop();
|
|
42
|
+
_.trys.pop(); continue;
|
|
43
|
+
}
|
|
44
|
+
op = body.call(thisArg, _);
|
|
45
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
46
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
50
|
+
exports.RemotePodsTransport = void 0;
|
|
51
|
+
/**
|
|
52
|
+
* RemotePodsTransport — reads pods from a FlowScale Operator HTTP API
|
|
53
|
+
* served by PodsOperatorServer running on another machine.
|
|
54
|
+
*
|
|
55
|
+
* Only `list()` and `get()` are functional. All other methods throw since
|
|
56
|
+
* process lifecycle management must be done on the machine running the operator.
|
|
57
|
+
*
|
|
58
|
+
* Instance `baseUrl` fields are automatically set to `http://operatorHost:port`
|
|
59
|
+
* so that callers can reach the ComfyUI instances across the network.
|
|
60
|
+
*
|
|
61
|
+
* Usage:
|
|
62
|
+
* import { RemotePodsTransport, LocalPodsAPI } from 'flowscale'
|
|
63
|
+
* const podsApi = new LocalPodsAPI(new RemotePodsTransport('http://192.168.1.100:3001'))
|
|
64
|
+
*/
|
|
65
|
+
var RemotePodsTransport = /** @class */ (function () {
|
|
66
|
+
function RemotePodsTransport(operatorUrl) {
|
|
67
|
+
this.operatorUrl = operatorUrl.replace(/\/$/, '');
|
|
68
|
+
try {
|
|
69
|
+
this.operatorHost = new URL(this.operatorUrl).hostname;
|
|
70
|
+
}
|
|
71
|
+
catch (_a) {
|
|
72
|
+
this.operatorHost = 'localhost';
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
RemotePodsTransport.prototype._injectBaseUrls = function (pod) {
|
|
76
|
+
var _this = this;
|
|
77
|
+
return __assign(__assign({}, pod), { instances: pod.instances.map(function (inst) { return (__assign(__assign({}, inst), { baseUrl: "http://".concat(_this.operatorHost, ":").concat(inst.port) })); }) });
|
|
78
|
+
};
|
|
79
|
+
RemotePodsTransport.prototype.list = function () {
|
|
80
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
81
|
+
var res, pods;
|
|
82
|
+
var _this = this;
|
|
83
|
+
return __generator(this, function (_a) {
|
|
84
|
+
switch (_a.label) {
|
|
85
|
+
case 0: return [4 /*yield*/, fetch("".concat(this.operatorUrl, "/api/pods"), {
|
|
86
|
+
signal: AbortSignal.timeout(5000),
|
|
87
|
+
})];
|
|
88
|
+
case 1:
|
|
89
|
+
res = _a.sent();
|
|
90
|
+
if (!res.ok)
|
|
91
|
+
throw new Error("Operator /api/pods failed (".concat(res.status, ")"));
|
|
92
|
+
return [4 /*yield*/, res.json()];
|
|
93
|
+
case 2:
|
|
94
|
+
pods = (_a.sent());
|
|
95
|
+
return [2 /*return*/, pods.map(function (p) { return _this._injectBaseUrls(p); })];
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
};
|
|
100
|
+
RemotePodsTransport.prototype.get = function (id) {
|
|
101
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
102
|
+
var res, pod;
|
|
103
|
+
return __generator(this, function (_a) {
|
|
104
|
+
switch (_a.label) {
|
|
105
|
+
case 0: return [4 /*yield*/, fetch("".concat(this.operatorUrl, "/api/pods/").concat(encodeURIComponent(id)), { signal: AbortSignal.timeout(5000) })];
|
|
106
|
+
case 1:
|
|
107
|
+
res = _a.sent();
|
|
108
|
+
if (res.status === 404)
|
|
109
|
+
return [2 /*return*/, null];
|
|
110
|
+
if (!res.ok)
|
|
111
|
+
throw new Error("Operator /api/pods/".concat(id, " failed (").concat(res.status, ")"));
|
|
112
|
+
return [4 /*yield*/, res.json()];
|
|
113
|
+
case 2:
|
|
114
|
+
pod = (_a.sent());
|
|
115
|
+
return [2 /*return*/, this._injectBaseUrls(pod)];
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
};
|
|
120
|
+
// ── Not available via remote operator ──────────────────────────────────────
|
|
121
|
+
RemotePodsTransport.prototype._notSupported = function (method) {
|
|
122
|
+
throw new Error("RemotePodsTransport: \"".concat(method, "\" is not available via remote operator \u2014 ") +
|
|
123
|
+
'connect to the operator machine directly to manage pods.');
|
|
124
|
+
};
|
|
125
|
+
RemotePodsTransport.prototype.create = function (_data) {
|
|
126
|
+
return this._notSupported('create');
|
|
127
|
+
};
|
|
128
|
+
RemotePodsTransport.prototype.update = function (_id, _data) {
|
|
129
|
+
return this._notSupported('update');
|
|
130
|
+
};
|
|
131
|
+
RemotePodsTransport.prototype.delete = function (_id) {
|
|
132
|
+
return this._notSupported('delete');
|
|
133
|
+
};
|
|
134
|
+
RemotePodsTransport.prototype.scanPaths = function () {
|
|
135
|
+
return this._notSupported('scanPaths');
|
|
136
|
+
};
|
|
137
|
+
RemotePodsTransport.prototype.scanPorts = function (_podId) {
|
|
138
|
+
return this._notSupported('scanPorts');
|
|
139
|
+
};
|
|
140
|
+
RemotePodsTransport.prototype.refreshInstance = function (_instanceId) {
|
|
141
|
+
return this._notSupported('refreshInstance');
|
|
142
|
+
};
|
|
143
|
+
RemotePodsTransport.prototype.startInstance = function (_req) {
|
|
144
|
+
return this._notSupported('startInstance');
|
|
145
|
+
};
|
|
146
|
+
RemotePodsTransport.prototype.stopInstance = function (_instanceId) {
|
|
147
|
+
return this._notSupported('stopInstance');
|
|
148
|
+
};
|
|
149
|
+
RemotePodsTransport.prototype.restartInstance = function (_instanceId) {
|
|
150
|
+
return this._notSupported('restartInstance');
|
|
151
|
+
};
|
|
152
|
+
RemotePodsTransport.prototype.deleteInstance = function (_instanceId) {
|
|
153
|
+
return this._notSupported('deleteInstance');
|
|
154
|
+
};
|
|
155
|
+
RemotePodsTransport.prototype.getSystemInfo = function () {
|
|
156
|
+
return this._notSupported('getSystemInfo');
|
|
157
|
+
};
|
|
158
|
+
RemotePodsTransport.prototype.getLogs = function (_instanceId) {
|
|
159
|
+
return this._notSupported('getLogs');
|
|
160
|
+
};
|
|
161
|
+
RemotePodsTransport.prototype.installRocmPyTorch = function (_podId) {
|
|
162
|
+
this._notSupported('installRocmPyTorch');
|
|
163
|
+
};
|
|
164
|
+
RemotePodsTransport.prototype.onInstanceStatusChanged = function (_cb) {
|
|
165
|
+
return function () { };
|
|
166
|
+
};
|
|
167
|
+
RemotePodsTransport.prototype.onLog = function (_cb) {
|
|
168
|
+
return function () { };
|
|
169
|
+
};
|
|
170
|
+
RemotePodsTransport.prototype.onRocmSetupDone = function (_cb) {
|
|
171
|
+
return function () { };
|
|
172
|
+
};
|
|
173
|
+
return RemotePodsTransport;
|
|
174
|
+
}());
|
|
175
|
+
exports.RemotePodsTransport = RemotePodsTransport;
|
package/package.json
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flowscale",
|
|
3
|
-
"version": "2.1.0-beta.
|
|
3
|
+
"version": "2.1.0-beta.3",
|
|
4
4
|
"description": "An NPM library for communicating with the Flowscale APIs",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"require": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts"
|
|
11
|
+
},
|
|
12
|
+
"./node": {
|
|
13
|
+
"require": "./dist/node-index.js",
|
|
14
|
+
"types": "./dist/node-index.d.ts"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
7
17
|
"keywords": [
|
|
8
18
|
"flowscale",
|
|
9
19
|
"api",
|
|
@@ -33,7 +43,16 @@
|
|
|
33
43
|
"axios": "^1.7.7",
|
|
34
44
|
"form-data": "^4.0.1"
|
|
35
45
|
},
|
|
46
|
+
"peerDependencies": {
|
|
47
|
+
"better-sqlite3": ">=9.0.0"
|
|
48
|
+
},
|
|
49
|
+
"peerDependenciesMeta": {
|
|
50
|
+
"better-sqlite3": {
|
|
51
|
+
"optional": true
|
|
52
|
+
}
|
|
53
|
+
},
|
|
36
54
|
"devDependencies": {
|
|
55
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
37
56
|
"@types/form-data": "^2.5.2",
|
|
38
57
|
"@types/node": "^22.9.1",
|
|
39
58
|
"typescript": "^5.6.3"
|