@openserv-labs/client 2.4.7 → 2.5.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/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ import "./deploy/index.js";
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,mBAAmB,CAAC"}
package/dist/cli.js ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ require("./deploy/index.js");
@@ -0,0 +1,46 @@
1
+ export declare class ApiError extends Error {
2
+ readonly statusCode: number | undefined;
3
+ constructor(message: string, statusCode: number | undefined);
4
+ }
5
+ export interface ApiClientOptions {
6
+ apiKey: string;
7
+ agentId?: number;
8
+ orchestratorUrl?: string;
9
+ }
10
+ export interface ContainerInfo {
11
+ id: string;
12
+ appName: string;
13
+ machineId: string;
14
+ status: string;
15
+ }
16
+ export interface StatusInfo {
17
+ id: string;
18
+ appName: string;
19
+ machineId: string;
20
+ status: string;
21
+ machineState: string;
22
+ metadata: Record<string, unknown>;
23
+ }
24
+ export interface ExecResult {
25
+ stdout: string;
26
+ stderr: string;
27
+ exitCode: number;
28
+ }
29
+ export interface GoLiveResult {
30
+ publicUrl: string;
31
+ }
32
+ export declare class ApiClient {
33
+ private client;
34
+ constructor(opts: ApiClientOptions);
35
+ createContainer(): Promise<ContainerInfo>;
36
+ getStatus(id: string): Promise<StatusInfo>;
37
+ findContainerByAgent(agentId: number): Promise<ContainerInfo | null>;
38
+ upload(id: string, tarBuffer: Buffer): Promise<void>;
39
+ exec(id: string, command: string[], timeout?: number): Promise<ExecResult>;
40
+ start(id: string, entrypoint?: string): Promise<void>;
41
+ restart(id: string): Promise<void>;
42
+ goLive(id: string, mode?: string): Promise<GoLiveResult>;
43
+ updateEndpointUrl(agentId: number, agentApiKey: string, endpointUrl: string): Promise<void>;
44
+ private request;
45
+ }
46
+ //# sourceMappingURL=api-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../../src/deploy/api-client.ts"],"names":[],"mappings":"AAIA,qBAAa,QAAS,SAAQ,KAAK;aAGf,UAAU,EAAE,MAAM,GAAG,SAAS;gBAD9C,OAAO,EAAE,MAAM,EACC,UAAU,EAAE,MAAM,GAAG,SAAS;CAKjD;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAgB;gBAElB,IAAI,EAAE,gBAAgB;IAgB5B,eAAe,IAAI,OAAO,CAAC,aAAa,CAAC;IAIzC,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAI1C,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAepE,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMpD,IAAI,CACR,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,MAAM,EAAE,EACjB,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,UAAU,CAAC;IAOhB,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMrD,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlC,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,SAAc,GAAG,OAAO,CAAC,YAAY,CAAC;IAM7D,iBAAiB,CACrB,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC;YAUF,OAAO;CAyBtB"}
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ApiClient = exports.ApiError = void 0;
7
+ const axios_1 = __importDefault(require("axios"));
8
+ const DEFAULT_ORCHESTRATOR_URL = "https://agent-orchestrator.openserv.ai";
9
+ class ApiError extends Error {
10
+ statusCode;
11
+ constructor(message, statusCode) {
12
+ super(message);
13
+ this.statusCode = statusCode;
14
+ this.name = "ApiError";
15
+ }
16
+ }
17
+ exports.ApiError = ApiError;
18
+ class ApiClient {
19
+ client;
20
+ constructor(opts) {
21
+ const headers = {
22
+ "x-openserv-key": opts.apiKey,
23
+ };
24
+ if (opts.agentId != null) {
25
+ headers["x-openserv-agent-id"] = String(opts.agentId);
26
+ }
27
+ this.client = axios_1.default.create({
28
+ baseURL: opts.orchestratorUrl || DEFAULT_ORCHESTRATOR_URL,
29
+ headers,
30
+ maxBodyLength: 100 * 1024 * 1024,
31
+ maxContentLength: 100 * 1024 * 1024,
32
+ });
33
+ }
34
+ async createContainer() {
35
+ return this.request("POST", "/container/create");
36
+ }
37
+ async getStatus(id) {
38
+ return this.request("GET", `/container/${id}/status`);
39
+ }
40
+ async findContainerByAgent(agentId) {
41
+ try {
42
+ const status = await this.getStatus(String(agentId));
43
+ return {
44
+ id: status.id,
45
+ appName: status.appName,
46
+ machineId: status.machineId,
47
+ status: status.status,
48
+ };
49
+ }
50
+ catch (err) {
51
+ if (err instanceof ApiError && err.statusCode === 404)
52
+ return null;
53
+ throw err;
54
+ }
55
+ }
56
+ async upload(id, tarBuffer) {
57
+ await this.request("POST", `/container/${id}/upload`, tarBuffer, {
58
+ headers: { "Content-Type": "application/gzip" },
59
+ });
60
+ }
61
+ async exec(id, command, timeout) {
62
+ return this.request("POST", `/container/${id}/exec`, {
63
+ command,
64
+ timeout,
65
+ });
66
+ }
67
+ async start(id, entrypoint) {
68
+ await this.request("POST", `/container/${id}/start`, {
69
+ entrypoint: entrypoint || "npx tsx src/agent.ts",
70
+ });
71
+ }
72
+ async restart(id) {
73
+ await this.request("POST", `/container/${id}/restart`);
74
+ }
75
+ async goLive(id, mode = "on-demand") {
76
+ return this.request("POST", `/container/${id}/go-live`, {
77
+ mode,
78
+ });
79
+ }
80
+ async updateEndpointUrl(agentId, agentApiKey, endpointUrl) {
81
+ const platformUrl = process.env.OPENSERV_API_URL || "https://api.openserv.ai";
82
+ await axios_1.default.put(`${platformUrl}/agents/${agentId}/endpoint-url`, { endpoint_url: endpointUrl }, { headers: { "x-openserv-key": agentApiKey } });
83
+ }
84
+ async request(method, path, body, config) {
85
+ try {
86
+ const res = await this.client.request({
87
+ method,
88
+ url: path,
89
+ data: body,
90
+ ...config,
91
+ });
92
+ return res.data;
93
+ }
94
+ catch (err) {
95
+ const axiosErr = err;
96
+ const statusCode = axiosErr.response?.status;
97
+ const data = axiosErr.response?.data ?? axiosErr.message;
98
+ const detail = typeof data === "string" ? data : JSON.stringify(data);
99
+ throw new ApiError(`${method} ${path} failed (${statusCode ?? "unknown"}): ${detail}`, statusCode);
100
+ }
101
+ }
102
+ }
103
+ exports.ApiClient = ApiClient;
@@ -0,0 +1,2 @@
1
+ export declare function deploy(targetPath: string): Promise<void>;
2
+ //# sourceMappingURL=deploy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/deploy/deploy.ts"],"names":[],"mappings":"AA8CA,wBAAsB,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAgH9D"}
@@ -0,0 +1,128 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.deploy = deploy;
7
+ const node_path_1 = __importDefault(require("node:path"));
8
+ const api_client_js_1 = require("./api-client.js");
9
+ const env_js_1 = require("./env.js");
10
+ const logger_js_1 = require("./logger.js");
11
+ const openserv_json_js_1 = require("./openserv-json.js");
12
+ const tar_js_1 = require("./tar.js");
13
+ async function resolveContainer(client, dir, containerId, agentId) {
14
+ if (containerId) {
15
+ logger_js_1.logger.info(`Using existing container: ${containerId}`);
16
+ return { id: containerId, isFirstDeploy: false };
17
+ }
18
+ if (agentId) {
19
+ logger_js_1.logger.info(`Agent ID found: ${agentId}. Checking for existing container...`);
20
+ const existing = await client.findContainerByAgent(agentId);
21
+ if (existing) {
22
+ (0, env_js_1.writeContainerId)(dir, existing.id);
23
+ logger_js_1.logger.info(` Found container: ${existing.id}`);
24
+ logger_js_1.logger.info(" Saved to .env\n");
25
+ return { id: existing.id, isFirstDeploy: false };
26
+ }
27
+ logger_js_1.logger.info(" No container found. Creating new container...");
28
+ }
29
+ else {
30
+ logger_js_1.logger.info("Creating new container...");
31
+ }
32
+ const container = await client.createContainer();
33
+ (0, env_js_1.writeContainerId)(dir, container.id);
34
+ logger_js_1.logger.info(` Container ID: ${container.id}`);
35
+ logger_js_1.logger.info(" Written to .env\n");
36
+ return { id: container.id, isFirstDeploy: true };
37
+ }
38
+ async function deploy(targetPath) {
39
+ const dir = node_path_1.default.resolve(targetPath);
40
+ logger_js_1.logger.info(`Deploying from ${dir}\n`);
41
+ const env = (0, env_js_1.readEnv)(dir);
42
+ const agentConfig = (0, openserv_json_js_1.readAgentConfig)(dir);
43
+ const agentId = agentConfig?.id;
44
+ if (!env.apiKey) {
45
+ throw new Error("OPENSERV_USER_API_KEY not found. Set it in your .env file or as an environment variable.");
46
+ }
47
+ const client = new api_client_js_1.ApiClient({
48
+ apiKey: env.apiKey,
49
+ agentId,
50
+ orchestratorUrl: env.orchestratorUrl,
51
+ });
52
+ const { id: targetId, isFirstDeploy } = await resolveContainer(client, dir, env.containerId, agentId);
53
+ let currentStatus;
54
+ let appName;
55
+ if (!isFirstDeploy) {
56
+ try {
57
+ const status = await client.getStatus(targetId);
58
+ currentStatus = status.status;
59
+ appName = status.appName;
60
+ logger_js_1.logger.info(` Current status: ${currentStatus}`);
61
+ }
62
+ catch {
63
+ // Container might not be reachable yet
64
+ }
65
+ }
66
+ logger_js_1.logger.info("\nCreating archive...");
67
+ const { buffer: tarBuffer, files } = await (0, tar_js_1.createTarBuffer)(dir);
68
+ for (const file of files) {
69
+ logger_js_1.logger.info(` ${file}`);
70
+ }
71
+ logger_js_1.logger.info(` ${files.length} files, ${(tarBuffer.length / 1024).toFixed(1)} KB`);
72
+ logger_js_1.logger.info("\nUploading files...");
73
+ await client.upload(targetId, tarBuffer);
74
+ logger_js_1.logger.info(" Done.");
75
+ const verify = await client.exec(targetId, ["ls", "-la", "/app"], 30);
76
+ if (verify.exitCode === 0) {
77
+ logger_js_1.logger.info(" Verified /app contents:");
78
+ for (const line of verify.stdout.split("\n").filter(Boolean)) {
79
+ logger_js_1.logger.info(` ${line}`);
80
+ }
81
+ }
82
+ else {
83
+ logger_js_1.logger.error(" Warning: could not verify upload");
84
+ if (verify.stderr)
85
+ logger_js_1.logger.error(` ${verify.stderr}`);
86
+ }
87
+ logger_js_1.logger.info("\nInstalling dependencies...");
88
+ const installResult = await client.exec(targetId, ["npm", "install"], 600);
89
+ if (installResult.exitCode !== 0) {
90
+ const parts = [`npm install failed (exit code ${installResult.exitCode})`];
91
+ if (installResult.stdout)
92
+ parts.push(`stdout: ${installResult.stdout.slice(0, 500)}`);
93
+ if (installResult.stderr)
94
+ parts.push(`stderr: ${installResult.stderr.slice(0, 500)}`);
95
+ throw new Error(parts.join("\n"));
96
+ }
97
+ logger_js_1.logger.info(" Done.");
98
+ const needsStart = isFirstDeploy || !currentStatus || currentStatus === "ready";
99
+ if (needsStart) {
100
+ logger_js_1.logger.info("\nStarting agent...");
101
+ await client.start(targetId);
102
+ logger_js_1.logger.info(" Agent started.");
103
+ }
104
+ else {
105
+ logger_js_1.logger.info("\nRestarting container...");
106
+ await client.restart(targetId);
107
+ logger_js_1.logger.info(" Container restarted.");
108
+ }
109
+ let publicUrl;
110
+ if (currentStatus !== "live") {
111
+ logger_js_1.logger.info("\nGoing live...");
112
+ const result = await client.goLive(targetId, "continuous");
113
+ publicUrl = result.publicUrl;
114
+ logger_js_1.logger.info(` Public URL: ${publicUrl}`);
115
+ }
116
+ else {
117
+ if (appName) {
118
+ publicUrl = `https://${appName}.fly.dev`;
119
+ }
120
+ logger_js_1.logger.info("\nAlready live.");
121
+ }
122
+ if (agentConfig?.apiKey && agentConfig.id && publicUrl) {
123
+ logger_js_1.logger.info("\nUpdating agent endpoint URL...");
124
+ await client.updateEndpointUrl(agentConfig.id, agentConfig.apiKey, publicUrl);
125
+ logger_js_1.logger.info(` Agent endpoint set to ${publicUrl}`);
126
+ }
127
+ logger_js_1.logger.info("\nDeploy complete!");
128
+ }
@@ -0,0 +1,8 @@
1
+ export interface EnvValues {
2
+ apiKey?: string;
3
+ containerId?: string;
4
+ orchestratorUrl?: string;
5
+ }
6
+ export declare function readEnv(dir: string): EnvValues;
7
+ export declare function writeContainerId(dir: string, containerId: string): void;
8
+ //# sourceMappingURL=env.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../src/deploy/env.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,SAAS;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAY9C;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAoBvE"}
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.readEnv = readEnv;
7
+ exports.writeContainerId = writeContainerId;
8
+ const node_fs_1 = __importDefault(require("node:fs"));
9
+ const node_path_1 = __importDefault(require("node:path"));
10
+ const dotenv_1 = require("dotenv");
11
+ function readEnv(dir) {
12
+ const envPath = node_path_1.default.join(dir, ".env");
13
+ const parsed = (0, dotenv_1.config)({ path: envPath });
14
+ const env = parsed.parsed ?? {};
15
+ return {
16
+ apiKey: env.OPENSERV_USER_API_KEY || process.env.OPENSERV_USER_API_KEY,
17
+ containerId: env.OPENSERV_CONTAINER_ID || process.env.OPENSERV_CONTAINER_ID,
18
+ orchestratorUrl: env.OPENSERV_ORCHESTRATOR_URL || process.env.OPENSERV_ORCHESTRATOR_URL,
19
+ };
20
+ }
21
+ function writeContainerId(dir, containerId) {
22
+ const envPath = node_path_1.default.join(dir, ".env");
23
+ let content = "";
24
+ if (node_fs_1.default.existsSync(envPath)) {
25
+ content = node_fs_1.default.readFileSync(envPath, "utf8");
26
+ }
27
+ const key = "OPENSERV_CONTAINER_ID";
28
+ const line = `${key}=${containerId}`;
29
+ const regex = new RegExp(`^${key}=.*$`, "m");
30
+ if (regex.test(content)) {
31
+ content = content.replace(regex, line);
32
+ }
33
+ else {
34
+ const separator = content.length > 0 && !content.endsWith("\n") ? "\n" : "";
35
+ content = `${content}${separator}${line}\n`;
36
+ }
37
+ node_fs_1.default.writeFileSync(envPath, content, "utf8");
38
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/deploy/index.ts"],"names":[],"mappings":""}
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const deploy_js_1 = require("./deploy.js");
4
+ const HELP = `
5
+ Usage: serv <command> [path]
6
+
7
+ Commands:
8
+ deploy [path] Deploy an agent to OpenServ (default path: .)
9
+
10
+ Options:
11
+ --help, -h Show this help message
12
+
13
+ Environment variables (set in .env or shell):
14
+ OPENSERV_USER_API_KEY Your OpenServ API key (required)
15
+ OPENSERV_CONTAINER_ID Container ID for redeployment (auto-set after first deploy)
16
+ OPENSERV_ORCHESTRATOR_URL Custom orchestrator URL (optional)
17
+ `.trim();
18
+ async function main() {
19
+ const args = process.argv.slice(2);
20
+ if (args.length === 0 || args.includes("--help") || args.includes("-h")) {
21
+ console.log(HELP);
22
+ process.exit(0);
23
+ }
24
+ const command = args[0];
25
+ switch (command) {
26
+ case "deploy": {
27
+ const targetPath = args[1] || ".";
28
+ await (0, deploy_js_1.deploy)(targetPath);
29
+ break;
30
+ }
31
+ default:
32
+ console.error(`Unknown command: ${command}\n`);
33
+ console.log(HELP);
34
+ process.exit(1);
35
+ }
36
+ }
37
+ main().catch((err) => {
38
+ console.error("\nFailed:", err instanceof Error ? err.message : err);
39
+ process.exit(1);
40
+ });
@@ -0,0 +1,6 @@
1
+ export declare const logger: {
2
+ info: (...args: unknown[]) => void;
3
+ warn: (...args: unknown[]) => void;
4
+ error: (...args: unknown[]) => void;
5
+ };
6
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/deploy/logger.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,MAAM;oBACD,OAAO,EAAE;oBACT,OAAO,EAAE;qBACR,OAAO,EAAE;CAC3B,CAAC"}
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.logger = void 0;
4
+ exports.logger = {
5
+ info: (...args) => console.log(...args),
6
+ warn: (...args) => console.warn(...args),
7
+ error: (...args) => console.error(...args),
8
+ };
@@ -0,0 +1,6 @@
1
+ export interface AgentConfig {
2
+ id: number;
3
+ apiKey?: string;
4
+ }
5
+ export declare function readAgentConfig(dir: string): AgentConfig | undefined;
6
+ //# sourceMappingURL=openserv-json.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openserv-json.d.ts","sourceRoot":"","sources":["../../src/deploy/openserv-json.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAmBpE"}
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.readAgentConfig = readAgentConfig;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ function readAgentConfig(dir) {
10
+ const filePath = node_path_1.default.join(dir, ".openserv.json");
11
+ if (!node_fs_1.default.existsSync(filePath)) {
12
+ return undefined;
13
+ }
14
+ try {
15
+ const raw = node_fs_1.default.readFileSync(filePath, "utf8");
16
+ const data = JSON.parse(raw);
17
+ if (!data.agents)
18
+ return undefined;
19
+ const firstAgent = Object.values(data.agents)[0];
20
+ if (!firstAgent)
21
+ return undefined;
22
+ return { id: firstAgent.id, apiKey: firstAgent.apiKey };
23
+ }
24
+ catch {
25
+ return undefined;
26
+ }
27
+ }
@@ -0,0 +1,6 @@
1
+ export interface TarResult {
2
+ buffer: Buffer;
3
+ files: string[];
4
+ }
5
+ export declare function createTarBuffer(dir: string): Promise<TarResult>;
6
+ //# sourceMappingURL=tar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tar.d.ts","sourceRoot":"","sources":["../../src/deploy/tar.ts"],"names":[],"mappings":"AAwBA,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAerE"}
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createTarBuffer = createTarBuffer;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const ignore_1 = __importDefault(require("ignore"));
10
+ const nanotar_1 = require("nanotar");
11
+ const ALWAYS_EXCLUDE = [
12
+ "node_modules",
13
+ ".git",
14
+ "dist",
15
+ ".next",
16
+ ".turbo",
17
+ ".env",
18
+ ".env.example",
19
+ ".env.local",
20
+ ".env.*.local",
21
+ ];
22
+ const ALWAYS_EXCLUDE_EXTENSIONS = [".tsbuildinfo"];
23
+ async function createTarBuffer(dir) {
24
+ const ig = (0, ignore_1.default)();
25
+ ig.add(ALWAYS_EXCLUDE);
26
+ const gitignorePath = node_path_1.default.join(dir, ".gitignore");
27
+ if (node_fs_1.default.existsSync(gitignorePath)) {
28
+ const content = node_fs_1.default.readFileSync(gitignorePath, "utf8");
29
+ ig.add(content);
30
+ }
31
+ const entries = collectEntries(dir, dir, ig);
32
+ const files = entries.map((e) => e.name);
33
+ const gzipped = await (0, nanotar_1.createTarGzip)(entries);
34
+ return { buffer: Buffer.from(gzipped), files };
35
+ }
36
+ function collectEntries(baseDir, currentDir, ig) {
37
+ const entries = [];
38
+ const items = node_fs_1.default.readdirSync(currentDir, { withFileTypes: true });
39
+ for (const item of items) {
40
+ const fullPath = node_path_1.default.join(currentDir, item.name);
41
+ const relativePath = node_path_1.default.relative(baseDir, fullPath);
42
+ if (ALWAYS_EXCLUDE_EXTENSIONS.some((ext) => item.name.endsWith(ext))) {
43
+ continue;
44
+ }
45
+ const testPath = item.isDirectory() ? `${relativePath}/` : relativePath;
46
+ if (ig.ignores(testPath)) {
47
+ continue;
48
+ }
49
+ if (item.isDirectory()) {
50
+ entries.push(...collectEntries(baseDir, fullPath, ig));
51
+ }
52
+ else {
53
+ entries.push({
54
+ name: relativePath,
55
+ data: new Uint8Array(node_fs_1.default.readFileSync(fullPath)),
56
+ });
57
+ }
58
+ }
59
+ return entries;
60
+ }
@@ -47,7 +47,7 @@ export interface AgentInstance {
47
47
  * };
48
48
  * ```
49
49
  */
50
- export interface ProvisionConfig {
50
+ interface ProvisionConfigBase {
51
51
  /** Agent configuration */
52
52
  agent: {
53
53
  /**
@@ -114,6 +114,16 @@ export interface ProvisionConfig {
114
114
  agentIds?: number[];
115
115
  };
116
116
  }
117
+ /**
118
+ * Pre-created user API key to skip SIWE authentication.
119
+ * When the workflow trigger is x402, walletAddress is also required
120
+ * so the platform knows which wallet to use for payments.
121
+ */
122
+ interface ProvisionConfigWithCredentials extends ProvisionConfigBase {
123
+ userApiKey: string;
124
+ walletAddress?: string;
125
+ }
126
+ export type ProvisionConfig = ProvisionConfigWithCredentials | ProvisionConfigBase;
117
127
  /**
118
128
  * Result from provisioning an agent and workflow.
119
129
  */
@@ -268,4 +278,5 @@ export declare function getProvisionedInfo(agentName: string, workflowName?: str
268
278
  * ```
269
279
  */
270
280
  export declare function clearProvisionedState(): void;
281
+ export {};
271
282
  //# sourceMappingURL=provision.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"provision.d.ts","sourceRoot":"","sources":["../src/provision.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"provision.d.ts","sourceRoot":"","sources":["../src/provision.ts"],"names":[],"mappings":"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"}
package/dist/provision.js CHANGED
@@ -155,6 +155,32 @@ function writeWalletToEnv(privateKey) {
155
155
  writeEnvVar("WALLET_PRIVATE_KEY", privateKey);
156
156
  }
157
157
  // ============================================================================
158
+ // State Initialization
159
+ // ============================================================================
160
+ /**
161
+ * Initialize the state file if it doesn't exist yet.
162
+ *
163
+ * Reads `OPENSERV_USER_API_KEY` from the environment and seeds
164
+ * `.openserv.json` with the key plus empty `agents` and `workflows` maps.
165
+ * If the state file already exists, this is a no-op.
166
+ */
167
+ function initState() {
168
+ const statePath = path.resolve(process.cwd(), STATE_FILE);
169
+ if (fs.existsSync(statePath)) {
170
+ return;
171
+ }
172
+ const userApiKey = process.env.OPENSERV_USER_API_KEY;
173
+ const state = {
174
+ agents: {},
175
+ workflows: {},
176
+ };
177
+ if (userApiKey) {
178
+ state.userApiKey = userApiKey;
179
+ }
180
+ writeState(state);
181
+ logger.info("Initialized state file");
182
+ }
183
+ // ============================================================================
158
184
  // Wallet Management
159
185
  // ============================================================================
160
186
  /**
@@ -483,6 +509,33 @@ async function provisionWorkflow(client, agentId, agentName, config) {
483
509
  // ============================================================================
484
510
  // Main Provision Function
485
511
  // ============================================================================
512
+ function validateProvisionCredentials(config) {
513
+ const hasApiKey = "userApiKey" in config && !!config.userApiKey;
514
+ const hasWallet = "walletAddress" in config && !!config.walletAddress;
515
+ if (hasWallet && !hasApiKey) {
516
+ throw new Error("walletAddress requires userApiKey to be provided");
517
+ }
518
+ if (hasApiKey && !hasWallet && config.workflow.trigger.type === "x402") {
519
+ throw new Error("walletAddress is required when using an x402 trigger with userApiKey");
520
+ }
521
+ }
522
+ async function initializePlatformClient(config) {
523
+ const hasApiKey = "userApiKey" in config && !!config.userApiKey;
524
+ if (hasApiKey) {
525
+ const { userApiKey, walletAddress } = config;
526
+ const client = new client_1.PlatformClient({ apiKey: userApiKey });
527
+ if (walletAddress) {
528
+ client.walletAddress = walletAddress;
529
+ }
530
+ const state = readState();
531
+ writeState({ ...state, userApiKey });
532
+ return client;
533
+ }
534
+ initState();
535
+ const { privateKey } = await getOrCreateWallet();
536
+ const { client } = await createAuthenticatedClient(privateKey);
537
+ return client;
538
+ }
486
539
  /**
487
540
  * Provision an agent and workflow on the OpenServ platform.
488
541
  *
@@ -524,11 +577,8 @@ async function provisionWorkflow(client, agentId, agentName, config) {
524
577
  * ```
525
578
  */
526
579
  async function provision(config) {
527
- // Get or create wallet
528
- const { privateKey } = await getOrCreateWallet();
529
- // Create authenticated client (reuses saved API key for session continuity)
530
- // walletAddress is set on client.walletAddress for x402 resolution
531
- const { client } = await createAuthenticatedClient(privateKey);
580
+ validateProvisionCredentials(config);
581
+ const client = await initializePlatformClient(config);
532
582
  // Provision agent (returns agentId, apiKey, and authToken)
533
583
  const { agentId, apiKey, authToken } = await provisionAgent(client, config.agent);
534
584
  // Bind credentials to the agent
package/package.json CHANGED
@@ -1,9 +1,12 @@
1
1
  {
2
2
  "name": "@openserv-labs/client",
3
- "version": "2.4.7",
3
+ "version": "2.5.0",
4
4
  "description": "OpenServ Platform Client - Manage agents, workflows, tasks, and triggers via the OpenServ API",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
+ "bin": {
8
+ "serv": "./dist/cli.js"
9
+ },
7
10
  "scripts": {
8
11
  "build": "tsc",
9
12
  "dev": "tsc --watch",
@@ -15,7 +18,8 @@
15
18
  "format": "prettier --write \"**/*.{ts,json,md}\"",
16
19
  "format:check": "prettier --check \"**/*.{ts,json,md}\"",
17
20
  "test": "node --import tsx --test test/**/*.test.ts",
18
- "test:unit": "node --import tsx --test test/unit/**/*.test.ts"
21
+ "test:unit": "node --import tsx --test test/unit/**/*.test.ts",
22
+ "deploy": "tsx src/cli.ts deploy"
19
23
  },
20
24
  "repository": {
21
25
  "type": "git",
@@ -38,6 +42,9 @@
38
42
  "license": "MIT",
39
43
  "dependencies": {
40
44
  "axios": "^1.6.8",
45
+ "dotenv": "^16.4.5",
46
+ "ignore": "^7.0.3",
47
+ "nanotar": "^0.2.0",
41
48
  "pinata": "^2.5.1",
42
49
  "viem": "^2.45.1"
43
50
  },
@@ -45,7 +52,6 @@
45
52
  "@eslint/js": "^9.39.2",
46
53
  "@tsconfig/strictest": "^2.0.3",
47
54
  "@types/node": "^22.10.2",
48
- "dotenv": "^16.4.5",
49
55
  "eslint": "^9.39.2",
50
56
  "eslint-config-prettier": "^9.1.0",
51
57
  "eslint-plugin-prettier": "^5.1.3",