@getpaseo/cli 0.3.1 → 0.4.0-beta.2

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.
Files changed (62) hide show
  1. package/dist/cli.js +0 -5
  2. package/dist/commands/hub/connect.d.ts +1 -1
  3. package/dist/commands/hub/deploy-bundle.d.ts +16 -0
  4. package/dist/commands/hub/deploy-bundle.js +248 -0
  5. package/dist/commands/hub/deploy.d.ts +3 -2
  6. package/dist/commands/hub/deploy.js +17 -10
  7. package/dist/commands/hub/hub-client/index.d.ts +18 -0
  8. package/dist/commands/hub/hub-client/index.js +88 -0
  9. package/dist/commands/hub/{client.d.ts → hub-client/internal/contracts.d.ts} +16 -22
  10. package/dist/commands/hub/hub-client/internal/contracts.js +47 -0
  11. package/dist/commands/hub/hub-client/internal/problem.d.ts +3 -0
  12. package/dist/commands/hub/hub-client/internal/problem.js +72 -0
  13. package/dist/commands/hub/hub-client/internal/transport.d.ts +15 -0
  14. package/dist/commands/hub/hub-client/internal/transport.js +30 -0
  15. package/dist/commands/hub/index.d.ts +1 -1
  16. package/dist/commands/hub/index.js +1 -1
  17. package/dist/commands/hub/login-flow.d.ts +1 -1
  18. package/dist/commands/hub/projects.d.ts +1 -1
  19. package/dist/commands/provider/diagnostic.d.ts +12 -0
  20. package/dist/commands/provider/diagnostic.js +31 -0
  21. package/dist/commands/provider/index.js +5 -0
  22. package/dist/commands/workspace/index.js +10 -0
  23. package/dist/commands/workspace/rename.d.ts +22 -0
  24. package/dist/commands/workspace/rename.js +61 -0
  25. package/package.json +4 -4
  26. package/dist/commands/chat/create.d.ts +0 -9
  27. package/dist/commands/chat/create.js +0 -23
  28. package/dist/commands/chat/delete.d.ts +0 -6
  29. package/dist/commands/chat/delete.js +0 -20
  30. package/dist/commands/chat/index.d.ts +0 -3
  31. package/dist/commands/chat/index.js +0 -47
  32. package/dist/commands/chat/inspect.d.ts +0 -6
  33. package/dist/commands/chat/inspect.js +0 -20
  34. package/dist/commands/chat/ls.d.ts +0 -6
  35. package/dist/commands/chat/ls.js +0 -20
  36. package/dist/commands/chat/post.d.ts +0 -9
  37. package/dist/commands/chat/post.js +0 -28
  38. package/dist/commands/chat/read.d.ts +0 -11
  39. package/dist/commands/chat/read.js +0 -40
  40. package/dist/commands/chat/schema.d.ts +0 -36
  41. package/dist/commands/chat/schema.js +0 -81
  42. package/dist/commands/chat/shared.d.ts +0 -19
  43. package/dist/commands/chat/shared.js +0 -118
  44. package/dist/commands/chat/wait.d.ts +0 -9
  45. package/dist/commands/chat/wait.js +0 -45
  46. package/dist/commands/hub/client.js +0 -234
  47. package/dist/commands/hub/deploy-input.d.ts +0 -17
  48. package/dist/commands/hub/deploy-input.js +0 -264
  49. package/dist/commands/loop/index.d.ts +0 -3
  50. package/dist/commands/loop/index.js +0 -18
  51. package/dist/commands/loop/inspect.d.ts +0 -13
  52. package/dist/commands/loop/inspect.js +0 -100
  53. package/dist/commands/loop/logs.d.ts +0 -8
  54. package/dist/commands/loop/logs.js +0 -69
  55. package/dist/commands/loop/ls.d.ts +0 -18
  56. package/dist/commands/loop/ls.js +0 -62
  57. package/dist/commands/loop/run.d.ts +0 -30
  58. package/dist/commands/loop/run.js +0 -162
  59. package/dist/commands/loop/stop.d.ts +0 -15
  60. package/dist/commands/loop/stop.js +0 -56
  61. package/dist/commands/loop/types.d.ts +0 -138
  62. package/dist/commands/loop/types.js +0 -2
package/dist/cli.js CHANGED
@@ -1,8 +1,6 @@
1
1
  import { Command, Option } from "commander";
2
2
  import { createAgentCommand } from "./commands/agent/index.js";
3
3
  import { createDaemonCommand } from "./commands/daemon/index.js";
4
- import { createChatCommand } from "./commands/chat/index.js";
5
- import { createLoopCommand } from "./commands/loop/index.js";
6
4
  import { createPermitCommand } from "./commands/permit/index.js";
7
5
  import { createProviderCommand } from "./commands/provider/index.js";
8
6
  import { createScheduleCommand } from "./commands/schedule/index.js";
@@ -107,13 +105,10 @@ export function createCli() {
107
105
  program.addCommand(createDaemonCommand());
108
106
  program.addCommand(createHubCommand());
109
107
  // Chat commands
110
- program.addCommand(createChatCommand());
111
108
  // Terminal commands
112
109
  program.addCommand(createTerminalCommand());
113
110
  // Workspace script commands
114
111
  program.addCommand(createScriptCommand());
115
- // Loop commands
116
- program.addCommand(createLoopCommand());
117
112
  // Schedule commands
118
113
  program.addCommand(createScheduleCommand());
119
114
  program.addCommand(createHeartbeatCommand());
@@ -1,5 +1,5 @@
1
1
  import type { Command } from "commander";
2
- import type { HubHttpClient } from "./client.js";
2
+ import type { HubHttpClient } from "./hub-client/index.js";
3
3
  import type { HubCredentialStore } from "./credentials.js";
4
4
  import type { HubDaemonConnection } from "./daemon-client.js";
5
5
  import { type HubReporter } from "./reporter.js";
@@ -0,0 +1,16 @@
1
+ export interface HubBundleFile {
2
+ path: string;
3
+ content: string;
4
+ }
5
+ export interface HubDeployBundle {
6
+ projectSlug: string;
7
+ files: readonly HubBundleFile[];
8
+ workflowCount: number;
9
+ }
10
+ interface DiscoverHubBundleInput {
11
+ cwd: string;
12
+ project?: string;
13
+ }
14
+ export declare function discoverHubBundle(input: DiscoverHubBundleInput): Promise<HubDeployBundle>;
15
+ export {};
16
+ //# sourceMappingURL=deploy-bundle.d.ts.map
@@ -0,0 +1,248 @@
1
+ import { lstat, readFile, readdir } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import YAML from "yaml";
4
+ import { HubCommandError } from "./error.js";
5
+ const HUB_RESOURCE_PATH = ".paseo/hub.yml";
6
+ const LEGACY_TOML_PATH = ".paseo/hub.toml";
7
+ const WORKFLOW_DIRECTORY = ".paseo/workflows";
8
+ const PARTIAL_DIRECTORY = `${WORKFLOW_DIRECTORY}/partials`;
9
+ const PROJECT_SLUG_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u;
10
+ export async function discoverHubBundle(input) {
11
+ const projectSlug = requireProjectSlug(input.project);
12
+ const root = path.resolve(input.cwd);
13
+ await rejectLegacyToml(root);
14
+ const resource = await readBundleFile(root, HUB_RESOURCE_PATH, {
15
+ missingCode: "HUB_RESOURCE_MISSING",
16
+ missingMessage: `${HUB_RESOURCE_PATH} does not exist. Run this command from the project root.`,
17
+ });
18
+ const workflowNames = await discoverWorkflowNames(root);
19
+ const workflows = await Promise.all(workflowNames.map((name) => readBundleFile(root, `${WORKFLOW_DIRECTORY}/${name}`)));
20
+ const partialPaths = collectPartialPaths(workflows);
21
+ const partials = await Promise.all(partialPaths.map((file) => readBundleFile(root, file)));
22
+ const files = [resource, ...workflows, ...partials].toSorted(compareBundleFiles);
23
+ assertUniqueBundlePaths(files);
24
+ return { projectSlug, files, workflowCount: workflows.length };
25
+ }
26
+ function requireProjectSlug(project) {
27
+ if (project === undefined) {
28
+ throw new HubCommandError("HUB_PROJECT_REQUIRED", "Project is required. Pass --project <slug>.");
29
+ }
30
+ if (!PROJECT_SLUG_PATTERN.test(project)) {
31
+ throw new HubCommandError("HUB_INVALID_PROJECT", "Project must be a bare slug such as my-project.");
32
+ }
33
+ return project;
34
+ }
35
+ async function rejectLegacyToml(root) {
36
+ try {
37
+ await lstat(path.join(root, LEGACY_TOML_PATH));
38
+ }
39
+ catch (error) {
40
+ if (errorCode(error) === "ENOENT")
41
+ return;
42
+ throw unreadablePath(LEGACY_TOML_PATH);
43
+ }
44
+ throw new HubCommandError("HUB_CONFIGURATION_EXTENSION_UNSUPPORTED", `${LEGACY_TOML_PATH} is not supported. Use ${HUB_RESOURCE_PATH}.`);
45
+ }
46
+ async function discoverWorkflowNames(root) {
47
+ const directory = path.join(root, WORKFLOW_DIRECTORY);
48
+ await requireSafeDirectory(root, directory, WORKFLOW_DIRECTORY, {
49
+ missingCode: "HUB_WORKFLOW_DIRECTORY_MISSING",
50
+ missingMessage: `${WORKFLOW_DIRECTORY} does not exist.`,
51
+ });
52
+ let entries;
53
+ try {
54
+ entries = await readdir(directory, { withFileTypes: true });
55
+ }
56
+ catch {
57
+ throw unreadablePath(WORKFLOW_DIRECTORY);
58
+ }
59
+ const names = [];
60
+ for (const entry of entries.toSorted((left, right) => compareText(left.name, right.name))) {
61
+ const bundlePath = `${WORKFLOW_DIRECTORY}/${entry.name}`;
62
+ if (entry.isSymbolicLink())
63
+ throw unsafePath(bundlePath);
64
+ if (entry.isDirectory()) {
65
+ if (entry.name === "partials")
66
+ continue;
67
+ throw new HubCommandError("HUB_WORKFLOW_PATH_UNSUPPORTED", `${bundlePath} is not a direct-child .yml workflow.`);
68
+ }
69
+ if (!entry.isFile())
70
+ throw regularFileRequired(bundlePath);
71
+ if (!entry.name.endsWith(".yml")) {
72
+ throw new HubCommandError("HUB_WORKFLOW_EXTENSION_UNSUPPORTED", `${bundlePath} must use the .yml extension.`);
73
+ }
74
+ names.push(entry.name);
75
+ }
76
+ if (names.length === 0) {
77
+ throw new HubCommandError("HUB_WORKFLOW_MISSING", `${WORKFLOW_DIRECTORY} must contain at least one direct-child .yml workflow.`);
78
+ }
79
+ return names;
80
+ }
81
+ function collectPartialPaths(workflows) {
82
+ const paths = new Set();
83
+ for (const workflow of workflows) {
84
+ let document;
85
+ try {
86
+ document = YAML.parse(workflow.content);
87
+ }
88
+ catch {
89
+ throw new HubCommandError("HUB_WORKFLOW_INVALID_YAML", `${workflow.path} is not valid YAML.`);
90
+ }
91
+ if (!isRecord(document) || !Array.isArray(document["steps"]))
92
+ continue;
93
+ for (const step of document["steps"]) {
94
+ if (!isRecord(step) || !Array.isArray(step["prompt"]))
95
+ continue;
96
+ for (const block of step["prompt"]) {
97
+ if (!isRecord(block) || !Object.hasOwn(block, "include"))
98
+ continue;
99
+ const include = block["include"];
100
+ if (typeof include !== "string") {
101
+ throw invalidPartialPath(workflow.path, String(include));
102
+ }
103
+ paths.add(resolvePartialPath(workflow.path, include));
104
+ }
105
+ }
106
+ }
107
+ return [...paths].sort(compareText);
108
+ }
109
+ function resolvePartialPath(workflowPath, include) {
110
+ const decoded = decodePartialPath(workflowPath, include);
111
+ if (/^(?:[a-zA-Z]:[\\/]|[\\/]{1,2})/u.test(decoded)) {
112
+ throw invalidPartialPath(workflowPath, include);
113
+ }
114
+ const segments = decoded.replaceAll("\\", "/").split("/");
115
+ if (segments.some((segment) => segment.length === 0 || segment === "." || segment === "..") ||
116
+ segments[0] !== "partials") {
117
+ throw invalidPartialPath(workflowPath, include);
118
+ }
119
+ return `${WORKFLOW_DIRECTORY}/${segments.join("/")}`;
120
+ }
121
+ function decodePartialPath(workflowPath, include) {
122
+ let decoded = include;
123
+ for (let attempt = 0; attempt < 8; attempt += 1) {
124
+ let next;
125
+ try {
126
+ next = decodeURIComponent(decoded);
127
+ }
128
+ catch {
129
+ throw invalidPartialPath(workflowPath, include);
130
+ }
131
+ if (next === decoded)
132
+ break;
133
+ decoded = next;
134
+ }
135
+ if (decoded.includes("\0") || /%[0-9a-f]{2}/iu.test(decoded)) {
136
+ throw invalidPartialPath(workflowPath, include);
137
+ }
138
+ return decoded;
139
+ }
140
+ async function readBundleFile(root, bundlePath, missing = {
141
+ missingCode: "HUB_BUNDLE_FILE_MISSING",
142
+ missingMessage: `${bundlePath} does not exist.`,
143
+ }) {
144
+ const absolutePath = path.join(root, ...bundlePath.split("/"));
145
+ await rejectSymlinkComponents(root, absolutePath, bundlePath);
146
+ let stats;
147
+ try {
148
+ stats = await lstat(absolutePath);
149
+ }
150
+ catch (error) {
151
+ if (errorCode(error) === "ENOENT") {
152
+ throw new HubCommandError(missing.missingCode, missing.missingMessage);
153
+ }
154
+ throw unreadablePath(bundlePath);
155
+ }
156
+ if (stats.isSymbolicLink())
157
+ throw unsafePath(bundlePath);
158
+ if (!stats.isFile())
159
+ throw regularFileRequired(bundlePath);
160
+ if (!hasReadPermission(stats.mode))
161
+ throw unreadablePath(bundlePath);
162
+ try {
163
+ return { path: bundlePath, content: (await readFile(absolutePath)).toString("utf8") };
164
+ }
165
+ catch {
166
+ throw unreadablePath(bundlePath);
167
+ }
168
+ }
169
+ async function requireSafeDirectory(root, absolutePath, bundlePath, missing) {
170
+ await rejectSymlinkComponents(root, absolutePath, bundlePath);
171
+ let stats;
172
+ try {
173
+ stats = await lstat(absolutePath);
174
+ }
175
+ catch (error) {
176
+ if (errorCode(error) === "ENOENT") {
177
+ throw new HubCommandError(missing.missingCode, missing.missingMessage);
178
+ }
179
+ throw unreadablePath(bundlePath);
180
+ }
181
+ if (stats.isSymbolicLink())
182
+ throw unsafePath(bundlePath);
183
+ if (!stats.isDirectory()) {
184
+ throw new HubCommandError("HUB_WORKFLOW_DIRECTORY_INVALID", `${bundlePath} must be a directory.`);
185
+ }
186
+ }
187
+ async function rejectSymlinkComponents(root, target, bundlePath) {
188
+ const relative = path.relative(root, target);
189
+ let current = root;
190
+ for (const segment of relative.split(path.sep).filter(Boolean)) {
191
+ current = path.join(current, segment);
192
+ try {
193
+ if ((await lstat(current)).isSymbolicLink())
194
+ throw unsafePath(bundlePath);
195
+ }
196
+ catch (error) {
197
+ if (error instanceof HubCommandError)
198
+ throw error;
199
+ if (errorCode(error) === "ENOENT")
200
+ return;
201
+ throw unreadablePath(bundlePath);
202
+ }
203
+ }
204
+ }
205
+ function assertUniqueBundlePaths(files) {
206
+ const seen = new Set();
207
+ for (const file of files) {
208
+ if (seen.has(file.path)) {
209
+ throw new HubCommandError("HUB_BUNDLE_PATH_DUPLICATE", `${file.path} appears more than once in the discovered bundle.`);
210
+ }
211
+ seen.add(file.path);
212
+ }
213
+ }
214
+ function invalidPartialPath(workflowPath, include) {
215
+ return new HubCommandError("HUB_PARTIAL_PATH_INVALID", `${workflowPath} includes an unsafe path outside ${PARTIAL_DIRECTORY}: ${include}`);
216
+ }
217
+ function unsafePath(bundlePath) {
218
+ return new HubCommandError("HUB_BUNDLE_UNSAFE_PATH", `${bundlePath} must not use a symlink.`);
219
+ }
220
+ function unreadablePath(bundlePath) {
221
+ return new HubCommandError("HUB_BUNDLE_UNREADABLE", `Could not read ${bundlePath}. Check the file and permissions.`);
222
+ }
223
+ function regularFileRequired(bundlePath) {
224
+ return new HubCommandError("HUB_BUNDLE_NOT_FILE", `${bundlePath} must be a regular file.`);
225
+ }
226
+ function compareBundleFiles(left, right) {
227
+ return compareText(left.path, right.path);
228
+ }
229
+ function compareText(left, right) {
230
+ if (left < right)
231
+ return -1;
232
+ if (left > right)
233
+ return 1;
234
+ return 0;
235
+ }
236
+ function hasReadPermission(mode) {
237
+ return (mode & 0o444) !== 0;
238
+ }
239
+ function errorCode(error) {
240
+ if (typeof error !== "object" || error === null)
241
+ return undefined;
242
+ const code = Reflect.get(error, "code");
243
+ return typeof code === "string" ? code : undefined;
244
+ }
245
+ function isRecord(value) {
246
+ return typeof value === "object" && value !== null && !Array.isArray(value);
247
+ }
248
+ //# sourceMappingURL=deploy-bundle.js.map
@@ -1,10 +1,9 @@
1
1
  import type { Command } from "commander";
2
2
  import { type SingleResult } from "../../output/index.js";
3
- import { HubHttpClient, type HubInstallResult, type HubValidationResult } from "./client.js";
3
+ import { HubHttpClient, type HubInstallResult, type HubValidationResult } from "./hub-client/index.js";
4
4
  import { type HubCredentialStore } from "./credentials.js";
5
5
  import { type HubReporter } from "./reporter.js";
6
6
  export interface HubDeployOptions {
7
- file?: string;
8
7
  project?: string;
9
8
  hub?: string;
10
9
  apiKey?: string;
@@ -27,9 +26,11 @@ export interface HubDeployCommandDependencies {
27
26
  }
28
27
  interface HubDeployResult extends HubInstallResult {
29
28
  origin: string;
29
+ workflows: number;
30
30
  }
31
31
  interface HubDryRunResult extends HubValidationResult {
32
32
  origin: string;
33
+ workflows: number;
33
34
  }
34
35
  export declare function runHubDeploy(options: HubDeployOptions, environment?: HubDeployEnvironment): Promise<SingleResult<HubDeployResult> | SingleResult<HubDryRunResult>>;
35
36
  export declare function addHubDeployCommand(hub: Command, dependencies: HubDeployCommandDependencies): void;
@@ -1,9 +1,9 @@
1
1
  import { withOutput } from "../../output/index.js";
2
2
  import { addJsonOption } from "../../utils/command-options.js";
3
3
  import { resolveHubCredential, resolveHubOrigin } from "./authority.js";
4
- import { HubHttpClient } from "./client.js";
4
+ import { HubHttpClient, } from "./hub-client/index.js";
5
5
  import { PrivateHubCredentialStore } from "./credentials.js";
6
- import { resolveHubDeployInput } from "./deploy-input.js";
6
+ import { discoverHubBundle } from "./deploy-bundle.js";
7
7
  import { processHubReporter, reportHubProgress } from "./reporter.js";
8
8
  import { addHubResolutionHelp } from "./help.js";
9
9
  const resultSchema = {
@@ -13,6 +13,7 @@ const resultSchema = {
13
13
  { header: "VERSION", field: "version" },
14
14
  { header: "VERSION ID", field: "versionId" },
15
15
  { header: "ACTIVE", field: "active" },
16
+ { header: "WORKFLOWS", field: "workflows" },
16
17
  { header: "HUB", field: "origin" },
17
18
  ],
18
19
  };
@@ -21,6 +22,7 @@ const validationSchema = {
21
22
  columns: [
22
23
  { header: "PROJECT", field: "projectSlug" },
23
24
  { header: "VALID", field: "valid" },
25
+ { header: "WORKFLOWS", field: "workflows" },
24
26
  { header: "HUB", field: "origin" },
25
27
  ],
26
28
  };
@@ -37,9 +39,8 @@ export async function runHubDeploy(options, environment = {
37
39
  credentials,
38
40
  };
39
41
  const origin = resolveHubOrigin(resolution);
40
- const deployInput = await resolveHubDeployInput({
42
+ const deployInput = await discoverHubBundle({
41
43
  cwd: environment.cwd,
42
- ...(options.file === undefined ? {} : { file: options.file }),
43
44
  ...(options.project === undefined ? {} : { project: options.project }),
44
45
  });
45
46
  const action = options.dryRun === true ? "Validating" : "Deploying";
@@ -52,23 +53,29 @@ export async function runHubDeploy(options, environment = {
52
53
  };
53
54
  if (options.dryRun === true) {
54
55
  const validated = await (environment.hub ?? new HubHttpClient()).validateConfiguration(request);
55
- return { type: "single", data: { ...validated, origin }, schema: validationSchema };
56
+ return {
57
+ type: "single",
58
+ data: { ...validated, workflows: deployInput.workflowCount, origin },
59
+ schema: validationSchema,
60
+ };
56
61
  }
57
62
  const deployed = await (environment.hub ?? new HubHttpClient()).installConfiguration(request);
58
- return { type: "single", data: { ...deployed, origin }, schema: resultSchema };
63
+ return {
64
+ type: "single",
65
+ data: { ...deployed, workflows: deployInput.workflowCount, origin },
66
+ schema: resultSchema,
67
+ };
59
68
  }
60
69
  export function addHubDeployCommand(hub, dependencies) {
61
70
  addJsonOption(addHubResolutionHelp(hub
62
71
  .command("deploy")
63
- .description("Install and activate a Hub configuration")
64
- .argument("[file]", "Hub configuration YAML", ".paseo/hub.yml")
72
+ .description("Discover, validate, and activate the canonical .paseo Hub bundle")
65
73
  .option("-p, --project <slug>", "Target project slug")
66
74
  .option("--hub <origin>", "Paseo Hub origin")
67
75
  .option("--api-key <secret>", "Organization API key")
68
76
  .option("--dry-run", "Validate without installing or activating"))).action(withOutput(async (...args) => {
69
- const file = args[0];
70
77
  const options = args.at(-2);
71
- return runHubDeploy({ ...options, file }, {
78
+ return runHubDeploy(options, {
72
79
  cwd: dependencies.cwd(),
73
80
  env: dependencies.env,
74
81
  credentials: dependencies.credentials,
@@ -0,0 +1,18 @@
1
+ import type { HubBundleFile } from "../deploy-bundle.js";
2
+ import { type CliAuthorization, type CliAuthorizationPoll, type HubInstallResult, type HubProject, type HubValidationResult } from "./internal/contracts.js";
3
+ export type { CliAuthorization, CliAuthorizationPoll, HubInstallResult, HubProject, HubValidationResult, } from "./internal/contracts.js";
4
+ interface HubConfigurationInput {
5
+ origin: string;
6
+ apiKey: string;
7
+ projectSlug: string;
8
+ files: readonly HubBundleFile[];
9
+ }
10
+ export declare class HubHttpClient {
11
+ startCliAuthorization(origin: string): Promise<CliAuthorization>;
12
+ pollCliAuthorization(origin: string, deviceCode: string, timeoutMilliseconds: number): Promise<CliAuthorizationPoll>;
13
+ listProjects(origin: string, apiKey: string): Promise<HubProject[]>;
14
+ installConfiguration(input: HubConfigurationInput): Promise<HubInstallResult>;
15
+ validateConfiguration(input: HubConfigurationInput): Promise<HubValidationResult>;
16
+ issueEnrollmentToken(origin: string, apiKey: string): Promise<string>;
17
+ }
18
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,88 @@
1
+ import { HubCommandError } from "../error.js";
2
+ import { authorizationPollSchema, authorizationSchema, enrollmentTokenSchema, installResponseSchema, projectsResponseSchema, validationResponseSchema, } from "./internal/contracts.js";
3
+ import { requestHub } from "./internal/transport.js";
4
+ export class HubHttpClient {
5
+ startCliAuthorization(origin) {
6
+ return requestHub({
7
+ origin,
8
+ path: "/api/v1/cli-authorizations",
9
+ method: "POST",
10
+ body: {},
11
+ successStatus: 201,
12
+ schema: authorizationSchema,
13
+ failureMessage: "Hub CLI login could not be started",
14
+ });
15
+ }
16
+ async pollCliAuthorization(origin, deviceCode, timeoutMilliseconds) {
17
+ try {
18
+ return await requestHub({
19
+ origin,
20
+ path: "/api/v1/cli-authorizations/poll",
21
+ method: "POST",
22
+ body: { deviceCode },
23
+ successStatus: 200,
24
+ schema: authorizationPollSchema,
25
+ timeoutMilliseconds,
26
+ failureMessage: "Hub CLI login polling failed",
27
+ });
28
+ }
29
+ catch (error) {
30
+ if (error instanceof HubCommandError && error.code === "HUB_NETWORK_ERROR") {
31
+ return { status: "retry_later" };
32
+ }
33
+ throw error;
34
+ }
35
+ }
36
+ async listProjects(origin, apiKey) {
37
+ const response = await requestHub({
38
+ origin,
39
+ path: "/api/v1/projects",
40
+ method: "GET",
41
+ apiKey,
42
+ successStatus: 200,
43
+ schema: projectsResponseSchema,
44
+ failureMessage: "Hub project listing failed",
45
+ });
46
+ return response.projects;
47
+ }
48
+ installConfiguration(input) {
49
+ return requestHub({
50
+ origin: input.origin,
51
+ path: "/api/v1/configurations/install",
52
+ method: "POST",
53
+ apiKey: input.apiKey,
54
+ body: configurationBody(input),
55
+ successStatus: 201,
56
+ schema: installResponseSchema,
57
+ failureMessage: "Hub deployment failed",
58
+ });
59
+ }
60
+ validateConfiguration(input) {
61
+ return requestHub({
62
+ origin: input.origin,
63
+ path: "/api/v1/configurations/validate",
64
+ method: "POST",
65
+ apiKey: input.apiKey,
66
+ body: configurationBody(input),
67
+ successStatus: 200,
68
+ schema: validationResponseSchema,
69
+ failureMessage: "Hub configuration validation failed",
70
+ });
71
+ }
72
+ async issueEnrollmentToken(origin, apiKey) {
73
+ const response = await requestHub({
74
+ origin,
75
+ path: "/api/v1/daemons/enrollment-tokens",
76
+ method: "POST",
77
+ apiKey,
78
+ successStatus: 201,
79
+ schema: enrollmentTokenSchema,
80
+ failureMessage: "Hub daemon enrollment authorization failed",
81
+ });
82
+ return response.token;
83
+ }
84
+ }
85
+ function configurationBody(input) {
86
+ return { projectSlug: input.projectSlug, files: input.files };
87
+ }
88
+ //# sourceMappingURL=index.js.map
@@ -1,6 +1,5 @@
1
1
  import { z } from "zod";
2
- import type { HubDeployPartial } from "./deploy-input.js";
3
- declare const authorizationSchema: z.ZodObject<{
2
+ export declare const authorizationSchema: z.ZodObject<{
4
3
  deviceCode: z.ZodString;
5
4
  userCode: z.ZodString;
6
5
  verificationUri: z.ZodURL;
@@ -8,7 +7,7 @@ declare const authorizationSchema: z.ZodObject<{
8
7
  expiresAt: z.ZodString;
9
8
  interval: z.ZodNumber;
10
9
  }, z.core.$strict>;
11
- declare const authorizationPollSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
10
+ export declare const authorizationPollSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
12
11
  status: z.ZodLiteral<"pending">;
13
12
  interval: z.ZodNumber;
14
13
  }, z.core.$strict>, z.ZodObject<{
@@ -36,36 +35,31 @@ declare const projectSchema: z.ZodObject<{
36
35
  slug: z.ZodString;
37
36
  name: z.ZodString;
38
37
  }, z.core.$strict>;
39
- declare const installResponseSchema: z.ZodObject<{
38
+ export declare const projectsResponseSchema: z.ZodObject<{
39
+ projects: z.ZodArray<z.ZodObject<{
40
+ id: z.ZodString;
41
+ slug: z.ZodString;
42
+ name: z.ZodString;
43
+ }, z.core.$strict>>;
44
+ }, z.core.$strict>;
45
+ export declare const installResponseSchema: z.ZodObject<{
40
46
  projectSlug: z.ZodString;
41
47
  version: z.ZodNumber;
42
48
  versionId: z.ZodString;
43
49
  active: z.ZodLiteral<true>;
44
50
  }, z.core.$strict>;
45
- declare const validationResponseSchema: z.ZodObject<{
51
+ export declare const validationResponseSchema: z.ZodObject<{
46
52
  projectSlug: z.ZodString;
47
53
  valid: z.ZodLiteral<true>;
48
54
  }, z.core.$strict>;
55
+ export declare const enrollmentTokenSchema: z.ZodObject<{
56
+ token: z.ZodString;
57
+ expiresAt: z.ZodString;
58
+ }, z.core.$strict>;
49
59
  export type CliAuthorization = z.infer<typeof authorizationSchema>;
50
60
  export type CliAuthorizationPoll = z.infer<typeof authorizationPollSchema>;
51
61
  export type HubProject = z.infer<typeof projectSchema>;
52
62
  export type HubInstallResult = z.infer<typeof installResponseSchema>;
53
63
  export type HubValidationResult = z.infer<typeof validationResponseSchema>;
54
- interface HubConfigurationInput {
55
- origin: string;
56
- apiKey: string;
57
- projectSlug: string;
58
- yaml: string;
59
- partials?: readonly HubDeployPartial[];
60
- }
61
- export declare class HubHttpClient {
62
- startCliAuthorization(origin: string): Promise<CliAuthorization>;
63
- pollCliAuthorization(origin: string, deviceCode: string, timeoutMilliseconds: number): Promise<CliAuthorizationPoll>;
64
- listProjects(origin: string, apiKey: string): Promise<HubProject[]>;
65
- installConfiguration(input: HubConfigurationInput): Promise<HubInstallResult>;
66
- validateConfiguration(input: HubConfigurationInput): Promise<HubValidationResult>;
67
- issueEnrollmentToken(origin: string, apiKey: string): Promise<string>;
68
- private request;
69
- }
70
64
  export {};
71
- //# sourceMappingURL=client.d.ts.map
65
+ //# sourceMappingURL=contracts.d.ts.map
@@ -0,0 +1,47 @@
1
+ import { z } from "zod";
2
+ const activationUrlSchema = z.url({ protocol: /^https?$/u });
3
+ export const authorizationSchema = z
4
+ .object({
5
+ deviceCode: z.string().min(32),
6
+ userCode: z.string().min(1),
7
+ verificationUri: activationUrlSchema,
8
+ verificationUriComplete: activationUrlSchema,
9
+ expiresAt: z.string().datetime(),
10
+ interval: z.number().int().min(1),
11
+ })
12
+ .strict();
13
+ export const authorizationPollSchema = z.discriminatedUnion("status", [
14
+ z.object({ status: z.literal("pending"), interval: z.number().int().min(1) }).strict(),
15
+ z.object({ status: z.literal("slow_down"), interval: z.number().int().min(1) }).strict(),
16
+ z
17
+ .object({
18
+ status: z.literal("authorized"),
19
+ interval: z.number().int().min(1),
20
+ credential: z.string().min(32),
21
+ organizationId: z.string().min(1),
22
+ })
23
+ .strict(),
24
+ z.object({ status: z.literal("denied"), interval: z.number().int().min(1) }).strict(),
25
+ z.object({ status: z.literal("expired"), interval: z.number().int().min(1) }).strict(),
26
+ z.object({ status: z.literal("disclosed"), interval: z.number().int().min(1) }).strict(),
27
+ z.object({ status: z.literal("retry_later") }).strict(),
28
+ ]);
29
+ const projectSchema = z
30
+ .object({ id: z.string().uuid(), slug: z.string().min(1), name: z.string().min(1) })
31
+ .strict();
32
+ export const projectsResponseSchema = z.object({ projects: z.array(projectSchema) }).strict();
33
+ export const installResponseSchema = z
34
+ .object({
35
+ projectSlug: z.string().min(1),
36
+ version: z.number().int().positive(),
37
+ versionId: z.string().uuid(),
38
+ active: z.literal(true),
39
+ })
40
+ .strict();
41
+ export const validationResponseSchema = z
42
+ .object({ projectSlug: z.string().min(1), valid: z.literal(true) })
43
+ .strict();
44
+ export const enrollmentTokenSchema = z
45
+ .object({ token: z.string().min(32), expiresAt: z.string().datetime() })
46
+ .strict();
47
+ //# sourceMappingURL=contracts.js.map
@@ -0,0 +1,3 @@
1
+ import { HubCommandError } from "../../error.js";
2
+ export declare function hubRequestFailure(response: Response, failureMessage: string, apiKey: string | undefined): Promise<HubCommandError>;
3
+ //# sourceMappingURL=problem.d.ts.map