@project-ajax/sdk 0.0.28
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/README.md +22 -0
- package/dist/block.d.ts +321 -0
- package/dist/block.d.ts.map +1 -0
- package/dist/block.js +0 -0
- package/dist/builder.d.ts +12 -0
- package/dist/builder.d.ts.map +1 -0
- package/dist/builder.js +22 -0
- package/dist/capabilities/slashCommand.d.ts +56 -0
- package/dist/capabilities/slashCommand.d.ts.map +1 -0
- package/dist/capabilities/slashCommand.js +32 -0
- package/dist/capabilities/sync.d.ts +58 -0
- package/dist/capabilities/sync.d.ts.map +1 -0
- package/dist/capabilities/sync.js +26 -0
- package/dist/capabilities/tool.d.ts +69 -0
- package/dist/capabilities/tool.d.ts.map +1 -0
- package/dist/capabilities/tool.js +75 -0
- package/dist/capabilities/tool.test.d.ts +2 -0
- package/dist/capabilities/tool.test.d.ts.map +1 -0
- package/dist/cli/api/client.d.ts +185 -0
- package/dist/cli/api/client.d.ts.map +1 -0
- package/dist/cli/api/client.js +315 -0
- package/dist/cli/api/result.d.ts +43 -0
- package/dist/cli/api/result.d.ts.map +1 -0
- package/dist/cli/api/result.js +43 -0
- package/dist/cli/bin/cli.d.ts +3 -0
- package/dist/cli/bin/cli.d.ts.map +1 -0
- package/dist/cli/bin/cli.js +5 -0
- package/dist/cli/commands/auth.d.ts +2 -0
- package/dist/cli/commands/auth.d.ts.map +1 -0
- package/dist/cli/commands/auth.impl.d.ts +10 -0
- package/dist/cli/commands/auth.impl.d.ts.map +1 -0
- package/dist/cli/commands/auth.impl.js +68 -0
- package/dist/cli/commands/auth.js +71 -0
- package/dist/cli/commands/bundle.d.ts +2 -0
- package/dist/cli/commands/bundle.d.ts.map +1 -0
- package/dist/cli/commands/bundle.impl.d.ts +2 -0
- package/dist/cli/commands/bundle.impl.d.ts.map +1 -0
- package/dist/cli/commands/bundle.impl.js +21 -0
- package/dist/cli/commands/bundle.js +23 -0
- package/dist/cli/commands/capabilities.d.ts +2 -0
- package/dist/cli/commands/capabilities.d.ts.map +1 -0
- package/dist/cli/commands/capabilities.impl.d.ts +3 -0
- package/dist/cli/commands/capabilities.impl.d.ts.map +1 -0
- package/dist/cli/commands/capabilities.impl.js +40 -0
- package/dist/cli/commands/capabilities.js +24 -0
- package/dist/cli/commands/deploy.d.ts +3 -0
- package/dist/cli/commands/deploy.d.ts.map +1 -0
- package/dist/cli/commands/deploy.impl.d.ts +2 -0
- package/dist/cli/commands/deploy.impl.d.ts.map +1 -0
- package/dist/cli/commands/deploy.impl.js +31 -0
- package/dist/cli/commands/deploy.js +16 -0
- package/dist/cli/commands/exec.d.ts +3 -0
- package/dist/cli/commands/exec.d.ts.map +1 -0
- package/dist/cli/commands/exec.impl.d.ts +7 -0
- package/dist/cli/commands/exec.impl.d.ts.map +1 -0
- package/dist/cli/commands/exec.impl.js +122 -0
- package/dist/cli/commands/exec.js +30 -0
- package/dist/cli/commands/runs.d.ts +2 -0
- package/dist/cli/commands/runs.d.ts.map +1 -0
- package/dist/cli/commands/runs.impl.d.ts +4 -0
- package/dist/cli/commands/runs.impl.d.ts.map +1 -0
- package/dist/cli/commands/runs.impl.js +71 -0
- package/dist/cli/commands/runs.js +45 -0
- package/dist/cli/commands/secrets.d.ts +2 -0
- package/dist/cli/commands/secrets.d.ts.map +1 -0
- package/dist/cli/commands/secrets.impl.d.ts +5 -0
- package/dist/cli/commands/secrets.impl.d.ts.map +1 -0
- package/dist/cli/commands/secrets.impl.js +93 -0
- package/dist/cli/commands/secrets.js +64 -0
- package/dist/cli/config.d.ts +38 -0
- package/dist/cli/config.d.ts.map +1 -0
- package/dist/cli/config.js +133 -0
- package/dist/cli/context.d.ts +15 -0
- package/dist/cli/context.d.ts.map +1 -0
- package/dist/cli/context.js +16 -0
- package/dist/cli/deploy.d.ts +25 -0
- package/dist/cli/deploy.d.ts.map +1 -0
- package/dist/cli/deploy.js +101 -0
- package/dist/cli/flags.d.ts +16 -0
- package/dist/cli/flags.d.ts.map +1 -0
- package/dist/cli/flags.js +24 -0
- package/dist/cli/handler.d.ts +14 -0
- package/dist/cli/handler.d.ts.map +1 -0
- package/dist/cli/handler.js +30 -0
- package/dist/cli/routes.d.ts +2 -0
- package/dist/cli/routes.d.ts.map +1 -0
- package/dist/cli/routes.js +44 -0
- package/dist/cli/utils/array.d.ts +2 -0
- package/dist/cli/utils/array.d.ts.map +1 -0
- package/dist/cli/utils/array.js +10 -0
- package/dist/cli/utils/string.d.ts +2 -0
- package/dist/cli/utils/string.d.ts.map +1 -0
- package/dist/cli/utils/string.js +12 -0
- package/dist/cli/writer.d.ts +48 -0
- package/dist/cli/writer.d.ts.map +1 -0
- package/dist/cli/writer.js +73 -0
- package/dist/error.d.ts +8 -0
- package/dist/error.d.ts.map +1 -0
- package/dist/error.js +11 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/schema.d.ts +45 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +14 -0
- package/dist/types.d.ts +11 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +0 -0
- package/package.json +79 -0
- package/src/block.ts +529 -0
- package/src/builder.ts +26 -0
- package/src/capabilities/slashCommand.ts +71 -0
- package/src/capabilities/sync.ts +76 -0
- package/src/capabilities/tool.test.ts +181 -0
- package/src/capabilities/tool.ts +145 -0
- package/src/cli/api/client.ts +588 -0
- package/src/cli/api/result.ts +71 -0
- package/src/cli/bin/cli.ts +7 -0
- package/src/cli/commands/auth.impl.ts +92 -0
- package/src/cli/commands/auth.ts +77 -0
- package/src/cli/commands/bundle.impl.ts +21 -0
- package/src/cli/commands/bundle.ts +23 -0
- package/src/cli/commands/capabilities.impl.ts +47 -0
- package/src/cli/commands/capabilities.ts +25 -0
- package/src/cli/commands/deploy.impl.ts +34 -0
- package/src/cli/commands/deploy.ts +16 -0
- package/src/cli/commands/exec.impl.ts +169 -0
- package/src/cli/commands/exec.ts +32 -0
- package/src/cli/commands/runs.impl.ts +87 -0
- package/src/cli/commands/runs.ts +49 -0
- package/src/cli/commands/secrets.impl.ts +124 -0
- package/src/cli/commands/secrets.ts +73 -0
- package/src/cli/config.ts +175 -0
- package/src/cli/context.ts +26 -0
- package/src/cli/deploy.ts +175 -0
- package/src/cli/flags.ts +43 -0
- package/src/cli/handler.ts +62 -0
- package/src/cli/routes.ts +43 -0
- package/src/cli/utils/array.ts +7 -0
- package/src/cli/utils/string.ts +9 -0
- package/src/cli/writer.ts +97 -0
- package/src/error.ts +12 -0
- package/src/index.ts +3 -0
- package/src/schema.ts +54 -0
- package/src/types.ts +10 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { exec } from "node:child_process";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { baseUrl } from "../api/client.js";
|
|
4
|
+
import type { Environment } from "../config.js";
|
|
5
|
+
import { buildHandler, type HandlerContext } from "../handler.js";
|
|
6
|
+
|
|
7
|
+
interface LoginFlags {
|
|
8
|
+
env?: Environment;
|
|
9
|
+
"base-url"?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Open a URL in the user's default browser or Notion app (if app available)
|
|
14
|
+
*/
|
|
15
|
+
async function openUrl(env: Environment, url: string): Promise<void> {
|
|
16
|
+
const platform = process.platform;
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
if (platform === "darwin") {
|
|
20
|
+
// Check if we should try to open Notion app on macOS
|
|
21
|
+
let notionAppName: string | null = null;
|
|
22
|
+
|
|
23
|
+
if (env === "prod" && existsSync("/Applications/Notion.app")) {
|
|
24
|
+
notionAppName = "Notion";
|
|
25
|
+
} else if (env === "dev" && existsSync("/Applications/Notion Dev.app")) {
|
|
26
|
+
notionAppName = "Notion Dev";
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (notionAppName) {
|
|
30
|
+
// Open the app first, then the URL (which will open in the app)
|
|
31
|
+
exec(`open -a "${notionAppName}" "${url}"`);
|
|
32
|
+
} else {
|
|
33
|
+
// Fall back to opening in default browser
|
|
34
|
+
exec(`open "${url}"`);
|
|
35
|
+
}
|
|
36
|
+
} else if (platform === "win32") {
|
|
37
|
+
exec(`start "" "${url}"`);
|
|
38
|
+
} else {
|
|
39
|
+
// Linux and other Unix-like systems
|
|
40
|
+
exec(`xdg-open "${url}"`);
|
|
41
|
+
}
|
|
42
|
+
} catch (error) {
|
|
43
|
+
throw new Error(`Failed to open browser: ${error}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const login = buildHandler(async function (
|
|
48
|
+
this: HandlerContext,
|
|
49
|
+
flags: LoginFlags,
|
|
50
|
+
token?: string,
|
|
51
|
+
) {
|
|
52
|
+
const environment = flags.env ?? "prod";
|
|
53
|
+
|
|
54
|
+
if (flags["base-url"]) {
|
|
55
|
+
await this.config.setBaseUrl(flags["base-url"]);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// If no token provided, open the browser to get one
|
|
59
|
+
if (!token) {
|
|
60
|
+
const url = `${baseUrl({
|
|
61
|
+
environment,
|
|
62
|
+
override: this.config.baseUrl,
|
|
63
|
+
})}/__workers__?createToken=true`;
|
|
64
|
+
|
|
65
|
+
this.writer.writeErr(`Opening browser to create a token...\n${url}\n`);
|
|
66
|
+
this.writer.writeErr("After creating a token, run:");
|
|
67
|
+
this.writer.writeErr("\tnpx workers auth login <token>");
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
await openUrl(environment, url);
|
|
71
|
+
} catch (_error) {
|
|
72
|
+
this.writer.writeErr(
|
|
73
|
+
`Failed to open browser automatically. Please visit:\n ${url}`,
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
await this.config.setEnvironment(environment);
|
|
81
|
+
await this.config.setToken(token);
|
|
82
|
+
|
|
83
|
+
this.writer.writeErr("Successfully logged in!");
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
export const show = buildHandler(function (this: HandlerContext) {
|
|
87
|
+
this.writer.writeOut(`${this.config.token ?? ""}`);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
export const logout = buildHandler(async function (this: HandlerContext) {
|
|
91
|
+
await this.config.setToken(null);
|
|
92
|
+
});
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { buildCommand, buildRouteMap } from "@stricli/core";
|
|
2
|
+
import { Environments } from "../config.js";
|
|
3
|
+
import { globalFlags } from "../flags.js";
|
|
4
|
+
|
|
5
|
+
export const authCommands = buildRouteMap({
|
|
6
|
+
docs: {
|
|
7
|
+
brief: "Commands for managing the CLI context",
|
|
8
|
+
},
|
|
9
|
+
routes: {
|
|
10
|
+
login: buildCommand({
|
|
11
|
+
docs: {
|
|
12
|
+
brief: "Login to the Project Ajax platform using a Workers API token",
|
|
13
|
+
},
|
|
14
|
+
|
|
15
|
+
parameters: {
|
|
16
|
+
positional: {
|
|
17
|
+
kind: "tuple",
|
|
18
|
+
parameters: [
|
|
19
|
+
{
|
|
20
|
+
brief: "A Workers API token",
|
|
21
|
+
parse: String,
|
|
22
|
+
placeholder: "api-token",
|
|
23
|
+
optional: true,
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
flags: {
|
|
29
|
+
env: {
|
|
30
|
+
kind: "enum",
|
|
31
|
+
values: Environments,
|
|
32
|
+
brief: "The environment to use for the command",
|
|
33
|
+
optional: true,
|
|
34
|
+
hidden: true,
|
|
35
|
+
},
|
|
36
|
+
"base-url": {
|
|
37
|
+
kind: "parsed",
|
|
38
|
+
parse: String,
|
|
39
|
+
brief: "The base URL to use for all API requests",
|
|
40
|
+
optional: true,
|
|
41
|
+
hidden: true,
|
|
42
|
+
},
|
|
43
|
+
...globalFlags,
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
loader: () => import("./auth.impl.js").then((m) => m.login),
|
|
48
|
+
}),
|
|
49
|
+
|
|
50
|
+
show: buildCommand({
|
|
51
|
+
docs: {
|
|
52
|
+
brief: "Get the token for the CLI",
|
|
53
|
+
},
|
|
54
|
+
parameters: {
|
|
55
|
+
flags: {
|
|
56
|
+
...globalFlags,
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
loader: () => import("./auth.impl.js").then((m) => m.show),
|
|
61
|
+
}),
|
|
62
|
+
|
|
63
|
+
logout: buildCommand({
|
|
64
|
+
docs: {
|
|
65
|
+
brief: "Unset the token for the CLI",
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
parameters: {
|
|
69
|
+
flags: {
|
|
70
|
+
...globalFlags,
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
loader: () => import("./auth.impl.js").then((m) => m.logout),
|
|
75
|
+
}),
|
|
76
|
+
},
|
|
77
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Readable } from "node:stream";
|
|
2
|
+
import { Result } from "../api/result.js";
|
|
3
|
+
import { buildAuthedHandler } from "../handler.js";
|
|
4
|
+
|
|
5
|
+
export const downloadBundle = buildAuthedHandler(async function () {
|
|
6
|
+
const { workerId } = this.config;
|
|
7
|
+
if (!workerId) {
|
|
8
|
+
throw new Error(
|
|
9
|
+
"No worker configured. Run 'workers deploy' first to create a worker.",
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const result = await this.apiClient.downloadWorkerBundle(workerId);
|
|
14
|
+
if (Result.isFail(result)) {
|
|
15
|
+
this.writer.writeErr(`✗ Failed to download bundle`);
|
|
16
|
+
this.writer.writeErr(`✗ ${result.error.message}`);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
Readable.fromWeb(result.value).pipe(this.process.stdout);
|
|
21
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { buildCommand, buildRouteMap } from "@stricli/core";
|
|
2
|
+
import { globalFlags } from "../flags.js";
|
|
3
|
+
|
|
4
|
+
export const bundleCommands = buildRouteMap({
|
|
5
|
+
docs: {
|
|
6
|
+
brief: "Commands for managing worker bundles",
|
|
7
|
+
},
|
|
8
|
+
routes: {
|
|
9
|
+
download: buildCommand({
|
|
10
|
+
docs: {
|
|
11
|
+
brief: "Download the bundle for the worker",
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
parameters: {
|
|
15
|
+
flags: {
|
|
16
|
+
...globalFlags,
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
loader: () => import("./bundle.impl.js").then((m) => m.downloadBundle),
|
|
21
|
+
}),
|
|
22
|
+
},
|
|
23
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Result } from "../api/result.js";
|
|
2
|
+
import type { FormatFlags } from "../flags.js";
|
|
3
|
+
import { buildAuthedHandler } from "../handler.js";
|
|
4
|
+
|
|
5
|
+
export const listCapabilities = buildAuthedHandler(async function (
|
|
6
|
+
flags: FormatFlags,
|
|
7
|
+
) {
|
|
8
|
+
const { workerId } = this.config;
|
|
9
|
+
if (!workerId) {
|
|
10
|
+
throw new Error(
|
|
11
|
+
"No worker configured. Run 'workers deploy' first to create a worker.",
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
this.process.stderr.write(`Fetching capabilities...`);
|
|
16
|
+
|
|
17
|
+
const result = await this.apiClient.listCapabilities(workerId);
|
|
18
|
+
|
|
19
|
+
if (Result.isSuccess(result)) {
|
|
20
|
+
this.process.stderr.write("OK\n\n");
|
|
21
|
+
|
|
22
|
+
const data = Result.unwrap(result);
|
|
23
|
+
if (data.capabilities.length === 0) {
|
|
24
|
+
this.writer.writeErr("No capabilities found for this worker.");
|
|
25
|
+
} else {
|
|
26
|
+
this.writer.writeTableOut({
|
|
27
|
+
headers: ["Key", "Tag"],
|
|
28
|
+
rows: data.capabilities.map((capability) => [
|
|
29
|
+
capability.key,
|
|
30
|
+
capability._tag,
|
|
31
|
+
]),
|
|
32
|
+
plain: flags.plain,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
} else {
|
|
36
|
+
this.process.stderr.write("ERROR\n\n");
|
|
37
|
+
|
|
38
|
+
// If it's a validation error, show the clean debug message
|
|
39
|
+
if (result.error.validationError) {
|
|
40
|
+
this.writer.writeErr(`✗ ${result.error.validationError.debugMessage}`);
|
|
41
|
+
throw new Error(result.error.validationError.debugMessage);
|
|
42
|
+
} else {
|
|
43
|
+
this.writer.writeErr(`✗ ${result.error.message}`);
|
|
44
|
+
throw new Error(result.error.message);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { buildCommand, buildRouteMap } from "@stricli/core";
|
|
2
|
+
import { formatFlags, globalFlags } from "../flags.js";
|
|
3
|
+
|
|
4
|
+
export const capabilitiesCommands = buildRouteMap({
|
|
5
|
+
docs: {
|
|
6
|
+
brief: "Commands for managing worker capabilities",
|
|
7
|
+
},
|
|
8
|
+
routes: {
|
|
9
|
+
list: buildCommand({
|
|
10
|
+
docs: {
|
|
11
|
+
brief: "List all capabilities for a worker",
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
parameters: {
|
|
15
|
+
flags: {
|
|
16
|
+
...globalFlags,
|
|
17
|
+
...formatFlags,
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
loader: () =>
|
|
22
|
+
import("./capabilities.impl.js").then((m) => m.listCapabilities),
|
|
23
|
+
}),
|
|
24
|
+
},
|
|
25
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Result } from "../api/result.js";
|
|
2
|
+
import { deployWorker } from "../deploy.js";
|
|
3
|
+
import { buildAuthedHandler } from "../handler.js";
|
|
4
|
+
|
|
5
|
+
export const deploy = buildAuthedHandler(async function () {
|
|
6
|
+
const { token } = this.config.tokenInfo;
|
|
7
|
+
const workerId = this.config.workerId;
|
|
8
|
+
|
|
9
|
+
const environment = this.config.environment;
|
|
10
|
+
if (!environment) {
|
|
11
|
+
// Unexpected to get here when token is set
|
|
12
|
+
throw new Error("Unexpected error: Environment not set");
|
|
13
|
+
}
|
|
14
|
+
const workerPath = this.process.cwd();
|
|
15
|
+
|
|
16
|
+
this.writer.writeErr("Deploying worker...");
|
|
17
|
+
|
|
18
|
+
const result = await deployWorker(this, {
|
|
19
|
+
workerPath,
|
|
20
|
+
workerId,
|
|
21
|
+
token,
|
|
22
|
+
environment,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
if (Result.isSuccess(result)) {
|
|
26
|
+
const { workerId } = Result.unwrap(result);
|
|
27
|
+
await this.config.setWorkerId(workerId);
|
|
28
|
+
this.writer.writeErr("✓ Successfully deployed worker");
|
|
29
|
+
} else {
|
|
30
|
+
this.writer.writeErr("✗ Failed to deploy worker");
|
|
31
|
+
this.writer.writeErr(result.error.message);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { buildCommand } from "@stricli/core";
|
|
2
|
+
import { globalFlags } from "../flags.js";
|
|
3
|
+
|
|
4
|
+
export default buildCommand({
|
|
5
|
+
docs: {
|
|
6
|
+
brief: "Deploy a worker to the Project Ajax platform",
|
|
7
|
+
},
|
|
8
|
+
|
|
9
|
+
parameters: {
|
|
10
|
+
flags: {
|
|
11
|
+
...globalFlags,
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
|
|
15
|
+
loader: () => import("./deploy.impl.js").then((m) => m.deploy),
|
|
16
|
+
});
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { Result } from "../api/result.js";
|
|
2
|
+
import type { GlobalFlags } from "../flags.js";
|
|
3
|
+
import { buildAuthedHandler } from "../handler.js";
|
|
4
|
+
import { chunkEvery } from "../utils/array.js";
|
|
5
|
+
|
|
6
|
+
const usageHint = `Usage: workers exec <capabilityName> <capabilityFunction> [<argName1>=<value1> <argName2>=<value2>...]`;
|
|
7
|
+
|
|
8
|
+
interface ExecFlags extends GlobalFlags {
|
|
9
|
+
stream: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const exec = buildAuthedHandler(async function (
|
|
13
|
+
flags: ExecFlags,
|
|
14
|
+
...args: readonly string[]
|
|
15
|
+
) {
|
|
16
|
+
const { workerId } = this.config;
|
|
17
|
+
if (!workerId) {
|
|
18
|
+
throw new Error(
|
|
19
|
+
"No worker configured. Run 'workers deploy' first to create a worker.",
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const capabilityName = args[0];
|
|
24
|
+
const capabilityFunction = args[1] ?? null;
|
|
25
|
+
const functionArgs = args.slice(2).flatMap((arg) => arg.split(/[=]/));
|
|
26
|
+
|
|
27
|
+
if (!capabilityName) {
|
|
28
|
+
throw usageError();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// If there's an odd number of remaining args, the first one is the function name
|
|
32
|
+
if (functionArgs.length % 2 !== 0) {
|
|
33
|
+
throw usageError();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const argsList = chunkEvery(functionArgs, 2).map(([key, value]) => {
|
|
37
|
+
if (!key || !value) {
|
|
38
|
+
throw usageError();
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
name: key,
|
|
42
|
+
value,
|
|
43
|
+
};
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Execute the capability
|
|
47
|
+
this.writer.writeErr(`Executing capability "${capabilityName}"...`);
|
|
48
|
+
|
|
49
|
+
if (flags.stream) {
|
|
50
|
+
const result = await this.apiClient.runCapability(
|
|
51
|
+
workerId,
|
|
52
|
+
capabilityName,
|
|
53
|
+
capabilityFunction,
|
|
54
|
+
argsList,
|
|
55
|
+
true,
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
if (Result.isSuccess(result)) {
|
|
59
|
+
const decoder = new TextDecoder();
|
|
60
|
+
let buffer = "";
|
|
61
|
+
|
|
62
|
+
const allOutput: string[] = [];
|
|
63
|
+
|
|
64
|
+
const onBodyLine = async (jsonLine: string) => {
|
|
65
|
+
try {
|
|
66
|
+
const parsedLine = JSON.parse(jsonLine) as
|
|
67
|
+
| {
|
|
68
|
+
_tag: "log";
|
|
69
|
+
event: {
|
|
70
|
+
stream: "stdout" | "stderr";
|
|
71
|
+
data: string;
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
| {
|
|
75
|
+
_tag: "result";
|
|
76
|
+
result: unknown;
|
|
77
|
+
}
|
|
78
|
+
| {
|
|
79
|
+
_tag: "error";
|
|
80
|
+
error: string;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
switch (parsedLine._tag) {
|
|
84
|
+
case "log":
|
|
85
|
+
if (parsedLine.event.stream === "stdout") {
|
|
86
|
+
allOutput.push(parsedLine.event.data);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
parsedLine.event.data
|
|
90
|
+
.trim()
|
|
91
|
+
.split("\n")
|
|
92
|
+
.forEach((dataLine) => {
|
|
93
|
+
this.writer.writeErr(
|
|
94
|
+
`[worker:${parsedLine.event.stream}] ${dataLine.trim()}`,
|
|
95
|
+
);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
break;
|
|
99
|
+
case "result":
|
|
100
|
+
this.writer.writeOut(JSON.stringify(parsedLine.result, null, 2));
|
|
101
|
+
break;
|
|
102
|
+
case "error":
|
|
103
|
+
this.writer.writeErr(`Error: ${parsedLine.error}`);
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
} catch (error) {
|
|
107
|
+
this.writer.writeErr(`Error parsing log line: ${jsonLine}: ${error}`);
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
for await (const log of result.value) {
|
|
112
|
+
const output = decoder.decode(log);
|
|
113
|
+
buffer += output;
|
|
114
|
+
|
|
115
|
+
const lines = buffer.split("\n");
|
|
116
|
+
// Keep the last element as buffer (might be empty or incomplete line)
|
|
117
|
+
buffer = lines.pop() ?? "";
|
|
118
|
+
|
|
119
|
+
// Process all complete lines
|
|
120
|
+
for (const line of lines) {
|
|
121
|
+
await onBodyLine(line);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Process any remaining content in buffer
|
|
126
|
+
if (buffer) {
|
|
127
|
+
await onBodyLine(buffer);
|
|
128
|
+
}
|
|
129
|
+
return;
|
|
130
|
+
} else {
|
|
131
|
+
this.writer.writeErr(`\n✗ Failed to execute capability`);
|
|
132
|
+
|
|
133
|
+
// If it's a validation error, show the clean debug message
|
|
134
|
+
if (result.error.validationError) {
|
|
135
|
+
this.writer.writeErr(`\t✗ HTTP Error: ${result.error.status}`);
|
|
136
|
+
this.writer.writeErr(
|
|
137
|
+
`\t✗ ${result.error.validationError.debugMessage}`,
|
|
138
|
+
);
|
|
139
|
+
this.writer.writeErr(`\n${usageHint}`);
|
|
140
|
+
throw new Error(result.error.validationError.debugMessage);
|
|
141
|
+
} else {
|
|
142
|
+
this.writer.writeErr(`\t✗ HTTP Error: ${result.error.status}`);
|
|
143
|
+
this.writer.writeErr(`\t✗ ${result.error.message}`);
|
|
144
|
+
throw new Error(result.error.message);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
} else {
|
|
148
|
+
const result = await this.apiClient.runCapability(
|
|
149
|
+
workerId,
|
|
150
|
+
capabilityName,
|
|
151
|
+
capabilityFunction,
|
|
152
|
+
argsList,
|
|
153
|
+
false,
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
if (Result.isSuccess(result)) {
|
|
157
|
+
const data = Result.unwrap(result);
|
|
158
|
+
this.writer.writeOut(JSON.stringify(data.result, null, 2));
|
|
159
|
+
} else {
|
|
160
|
+
this.writer.writeErr(`\n✗ Failed to execute capability`);
|
|
161
|
+
this.writer.writeErr(`\t✗ ${result.error.message}`);
|
|
162
|
+
throw new Error(result.error.message);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
function usageError() {
|
|
168
|
+
return new Error(`Invalid arguments provided. ${usageHint}`);
|
|
169
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { buildCommand } from "@stricli/core";
|
|
2
|
+
import { globalFlags } from "../flags.js";
|
|
3
|
+
|
|
4
|
+
export default buildCommand({
|
|
5
|
+
docs: {
|
|
6
|
+
brief: "Execute a worker capability with optional function and arguments",
|
|
7
|
+
},
|
|
8
|
+
|
|
9
|
+
parameters: {
|
|
10
|
+
positional: {
|
|
11
|
+
kind: "array",
|
|
12
|
+
parameter: {
|
|
13
|
+
brief:
|
|
14
|
+
"Capability name, optional function name, and arguments (key value pairs)",
|
|
15
|
+
parse: String,
|
|
16
|
+
placeholder: "capabilityName [functionName] [key value]...",
|
|
17
|
+
},
|
|
18
|
+
minimum: 1,
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
flags: {
|
|
22
|
+
...globalFlags,
|
|
23
|
+
stream: {
|
|
24
|
+
kind: "boolean",
|
|
25
|
+
brief: "Stream the output of the capability",
|
|
26
|
+
default: true,
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
loader: () => import("./exec.impl.js").then((m) => m.exec),
|
|
32
|
+
});
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { Result } from "../api/result.js";
|
|
2
|
+
import type { FormatFlags, GlobalFlags } from "../flags.js";
|
|
3
|
+
import { buildAuthedHandler } from "../handler.js";
|
|
4
|
+
|
|
5
|
+
export const listRuns = buildAuthedHandler(async function (flags: FormatFlags) {
|
|
6
|
+
const { workerId } = this.config;
|
|
7
|
+
if (!workerId) {
|
|
8
|
+
throw new Error(
|
|
9
|
+
"No worker configured. Run 'workers deploy' first to create a worker.",
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
this.process.stderr.write("Fetching runs...");
|
|
14
|
+
|
|
15
|
+
const result = await this.apiClient.listRunsForWorker(workerId);
|
|
16
|
+
|
|
17
|
+
if (Result.isSuccess(result)) {
|
|
18
|
+
this.process.stderr.write("OK\n\n");
|
|
19
|
+
|
|
20
|
+
const data = Result.unwrap(result);
|
|
21
|
+
if (data.runs.length === 0) {
|
|
22
|
+
this.writer.writeErr("No runs found for this worker.");
|
|
23
|
+
} else {
|
|
24
|
+
this.writer.writeTableOut({
|
|
25
|
+
headers: ["Run ID", "Name", "Started At", "Ended At", "Exit Code"],
|
|
26
|
+
rows: data.runs.map((run) => [
|
|
27
|
+
run.runId,
|
|
28
|
+
run.name,
|
|
29
|
+
run.startedAt,
|
|
30
|
+
run.endedAt,
|
|
31
|
+
run.exitCode,
|
|
32
|
+
]),
|
|
33
|
+
plain: flags.plain,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
} else {
|
|
37
|
+
this.process.stderr.write("ERROR\n\n");
|
|
38
|
+
|
|
39
|
+
// If it's a validation error, show the clean debug message
|
|
40
|
+
if (result.error.validationError) {
|
|
41
|
+
this.writer.writeErr(`✗ ${result.error.validationError.debugMessage}`);
|
|
42
|
+
throw new Error(result.error.validationError.debugMessage);
|
|
43
|
+
} else {
|
|
44
|
+
this.writer.writeErr(`✗ ${result.error.message}`);
|
|
45
|
+
throw new Error(result.error.message);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
export const getRunLogs = buildAuthedHandler(async function (
|
|
51
|
+
_flags: GlobalFlags,
|
|
52
|
+
runId: string,
|
|
53
|
+
) {
|
|
54
|
+
const { workerId } = this.config;
|
|
55
|
+
if (!workerId) {
|
|
56
|
+
throw new Error(
|
|
57
|
+
"No worker configured. Run 'workers deploy' first to create a worker.",
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (!runId) {
|
|
62
|
+
throw new Error("Run ID is required");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
this.process.stderr.write(`Fetching logs for run ${runId}...`);
|
|
66
|
+
|
|
67
|
+
const result = await this.apiClient.getRunLogs(workerId, runId);
|
|
68
|
+
|
|
69
|
+
if (Result.isSuccess(result)) {
|
|
70
|
+
this.process.stderr.write("OK\n\n");
|
|
71
|
+
|
|
72
|
+
const data = Result.unwrap(result);
|
|
73
|
+
// Output logs directly to stdout (primary output)
|
|
74
|
+
this.writer.writeOut(data.logs);
|
|
75
|
+
} else {
|
|
76
|
+
this.process.stderr.write("ERROR\n\n");
|
|
77
|
+
|
|
78
|
+
// If it's a validation error, show the clean debug message
|
|
79
|
+
if (result.error.validationError) {
|
|
80
|
+
this.writer.writeErr(`✗ ${result.error.validationError.debugMessage}`);
|
|
81
|
+
throw new Error(result.error.validationError.debugMessage);
|
|
82
|
+
} else {
|
|
83
|
+
this.writer.writeErr(`✗ ${result.error.message}`);
|
|
84
|
+
throw new Error(result.error.message);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { buildCommand, buildRouteMap } from "@stricli/core";
|
|
2
|
+
import { formatFlags, globalFlags } from "../flags.js";
|
|
3
|
+
|
|
4
|
+
export const runsCommands = buildRouteMap({
|
|
5
|
+
docs: {
|
|
6
|
+
brief: "Commands for managing worker runs",
|
|
7
|
+
},
|
|
8
|
+
routes: {
|
|
9
|
+
list: buildCommand({
|
|
10
|
+
docs: {
|
|
11
|
+
brief: "List all runs for a worker",
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
parameters: {
|
|
15
|
+
flags: {
|
|
16
|
+
...globalFlags,
|
|
17
|
+
...formatFlags,
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
loader: () => import("./runs.impl.js").then((m) => m.listRuns),
|
|
22
|
+
}),
|
|
23
|
+
|
|
24
|
+
logs: buildCommand({
|
|
25
|
+
docs: {
|
|
26
|
+
brief: "Get logs for a specific run",
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
parameters: {
|
|
30
|
+
positional: {
|
|
31
|
+
kind: "tuple",
|
|
32
|
+
parameters: [
|
|
33
|
+
{
|
|
34
|
+
brief: "The run ID to get logs for",
|
|
35
|
+
parse: String,
|
|
36
|
+
placeholder: "runId",
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
flags: {
|
|
42
|
+
...globalFlags,
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
loader: () => import("./runs.impl.js").then((m) => m.getRunLogs),
|
|
47
|
+
}),
|
|
48
|
+
},
|
|
49
|
+
});
|