@otto-code/cli 0.8.19 → 0.9.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.
- package/bin/otto +2 -2
- package/dist/cli.js +13 -0
- package/dist/commands/architectural-view/deliver.d.ts +18 -0
- package/dist/commands/architectural-view/deliver.js +60 -0
- package/dist/commands/architectural-view/draft.d.ts +33 -0
- package/dist/commands/architectural-view/draft.js +128 -0
- package/dist/commands/architectural-view/index.d.ts +3 -0
- package/dist/commands/architectural-view/index.js +48 -0
- package/dist/commands/architectural-view/shared.d.ts +7 -0
- package/dist/commands/architectural-view/shared.js +27 -0
- package/dist/commands/artifact/cancel.d.ts +13 -0
- package/dist/commands/artifact/cancel.js +38 -0
- package/dist/commands/artifact/create.d.ts +21 -0
- package/dist/commands/artifact/create.js +56 -0
- package/dist/commands/artifact/get-data.d.ts +5 -0
- package/dist/commands/artifact/get-data.js +24 -0
- package/dist/commands/artifact/index.d.ts +3 -0
- package/dist/commands/artifact/index.js +55 -0
- package/dist/commands/artifact/ls.d.ts +20 -0
- package/dist/commands/artifact/ls.js +72 -0
- package/dist/commands/artifact/move.d.ts +15 -0
- package/dist/commands/artifact/move.js +41 -0
- package/dist/commands/artifact/regenerate.d.ts +12 -0
- package/dist/commands/artifact/regenerate.js +32 -0
- package/dist/commands/artifact/repair.d.ts +13 -0
- package/dist/commands/artifact/repair.js +38 -0
- package/dist/commands/artifact/shared.d.ts +10 -0
- package/dist/commands/artifact/shared.js +62 -0
- package/dist/commands/artifact/update-data.d.ts +13 -0
- package/dist/commands/artifact/update-data.js +43 -0
- package/dist/commands/daemon/index.js +3 -1
- package/dist/commands/daemon/reload.d.ts +10 -0
- package/dist/commands/daemon/reload.js +36 -0
- package/dist/commands/hub/daemon-client.d.ts +7 -0
- package/dist/commands/hub/hub-client/index.d.ts +3 -2
- package/dist/commands/hub/hub-client/index.js +12 -1
- package/dist/commands/hub/hub-client/internal/contracts.d.ts +17 -0
- package/dist/commands/hub/hub-client/internal/contracts.js +14 -0
- package/dist/commands/hub/index.js +3 -1
- package/dist/commands/hub/init-plan.d.ts +3 -0
- package/dist/commands/hub/init-plan.js +8 -4
- package/dist/commands/hub/init.d.ts +18 -3
- package/dist/commands/hub/init.js +218 -78
- package/dist/commands/hub/login.d.ts +2 -0
- package/dist/commands/hub/login.js +8 -2
- package/dist/commands/hub/starter-agent-runtime.d.ts +37 -0
- package/dist/commands/hub/starter-agent-runtime.js +49 -0
- package/dist/commands/hub/starter-trigger.d.ts +10 -0
- package/dist/commands/hub/starter-trigger.js +28 -0
- package/dist/commands/plugin/index.d.ts +14 -0
- package/dist/commands/plugin/index.js +84 -0
- package/dist/commands/plugin/scaffold.d.ts +6 -0
- package/dist/commands/plugin/scaffold.js +329 -0
- package/dist/commands/plugin/shared.d.ts +4 -0
- package/dist/commands/plugin/shared.js +26 -0
- package/dist/commands/project/create.d.ts +10 -0
- package/dist/commands/project/create.js +41 -0
- package/dist/commands/project/delete.d.ts +9 -0
- package/dist/commands/project/delete.js +33 -0
- package/dist/commands/project/index.d.ts +3 -0
- package/dist/commands/project/index.js +28 -0
- package/dist/commands/project/ls.d.ts +5 -0
- package/dist/commands/project/ls.js +19 -0
- package/dist/commands/project/rename.d.ts +15 -0
- package/dist/commands/project/rename.js +50 -0
- package/dist/commands/project/shared.d.ts +11 -0
- package/dist/commands/project/shared.js +18 -0
- package/dist/commands/schedule/shared.js +3 -0
- package/dist/commands/schedule/types.d.ts +4 -0
- package/dist/commands/workflow/graph.d.ts +85 -0
- package/dist/commands/workflow/graph.js +462 -0
- package/dist/commands/workflow/index.d.ts +3 -0
- package/dist/commands/workflow/index.js +43 -0
- package/dist/output/index.d.ts +1 -1
- package/dist/output/pairing.js +1 -1
- package/dist/output/render.js +6 -0
- package/dist/output/types.d.ts +14 -0
- package/package.json +5 -5
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { connectArtifactClient, toArtifactCommandError, } from "./shared.js";
|
|
3
|
+
const artifactListSchema = {
|
|
4
|
+
idField: "id",
|
|
5
|
+
columns: [
|
|
6
|
+
{ header: "ID", field: "id", width: 16 },
|
|
7
|
+
{ header: "NAME", field: "name", width: 28 },
|
|
8
|
+
{ header: "PROJECT", field: "project", width: 28 },
|
|
9
|
+
{ header: "STORED", field: "stored", width: 14 },
|
|
10
|
+
{ header: "SOURCE", field: "source", width: 12 },
|
|
11
|
+
{ header: "STATUS", field: "status", width: 12 },
|
|
12
|
+
{ header: "UPDATED", field: "updatedAt", width: 24 },
|
|
13
|
+
],
|
|
14
|
+
};
|
|
15
|
+
function storageLabel(location) {
|
|
16
|
+
switch (location) {
|
|
17
|
+
case "repository":
|
|
18
|
+
return "Repository";
|
|
19
|
+
case "host":
|
|
20
|
+
return "This host";
|
|
21
|
+
default:
|
|
22
|
+
return "Legacy";
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function sourceLabel(source) {
|
|
26
|
+
if (!source)
|
|
27
|
+
return "-";
|
|
28
|
+
switch (source.kind) {
|
|
29
|
+
case "chat":
|
|
30
|
+
return "Chat";
|
|
31
|
+
case "schedule":
|
|
32
|
+
return "Schedule";
|
|
33
|
+
case "workflow":
|
|
34
|
+
return "Workflow";
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export function toArtifactRow(artifact) {
|
|
38
|
+
return {
|
|
39
|
+
id: artifact.id,
|
|
40
|
+
name: artifact.name,
|
|
41
|
+
project: artifact.projectId,
|
|
42
|
+
stored: storageLabel(artifact.storageLocation),
|
|
43
|
+
source: sourceLabel(artifact.source),
|
|
44
|
+
status: artifact.status,
|
|
45
|
+
updatedAt: artifact.updatedAt,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
export async function runArtifactListCommand(options, _command) {
|
|
49
|
+
// The daemon filters by project root path, not registry id. A registry id
|
|
50
|
+
// would be resolved as a relative path under the daemon's cwd and silently
|
|
51
|
+
// match nothing, so refuse anything that is not an absolute path up front.
|
|
52
|
+
if (options.project !== undefined && !path.isAbsolute(options.project)) {
|
|
53
|
+
throw {
|
|
54
|
+
code: "ARTIFACT_PROJECT_INVALID",
|
|
55
|
+
message: `--project must be an absolute project root path, received ${JSON.stringify(options.project)}`,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
const client = await connectArtifactClient(options.host, "artifacts");
|
|
59
|
+
try {
|
|
60
|
+
const payload = await client.artifactList(options.project === undefined ? undefined : { projectId: options.project });
|
|
61
|
+
if (!payload.success)
|
|
62
|
+
throw new Error(payload.error ?? "Artifact list is unavailable");
|
|
63
|
+
return { type: "list", data: payload.artifacts.map(toArtifactRow), schema: artifactListSchema };
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
throw toArtifactCommandError("ARTIFACT_LIST_FAILED", "list artifacts", error);
|
|
67
|
+
}
|
|
68
|
+
finally {
|
|
69
|
+
await client.close().catch(() => { });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=ls.js.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
import type { SingleResult } from "../../output/index.js";
|
|
3
|
+
import { type ArtifactCommandOptions } from "./shared.js";
|
|
4
|
+
interface MoveArtifactOptions extends ArtifactCommandOptions {
|
|
5
|
+
to?: string;
|
|
6
|
+
}
|
|
7
|
+
interface MoveArtifactResult {
|
|
8
|
+
id: string;
|
|
9
|
+
storageLocation: "repository" | "host" | null;
|
|
10
|
+
status: string;
|
|
11
|
+
updatedAt: string;
|
|
12
|
+
}
|
|
13
|
+
export declare function runMoveCommand(id: string, options: MoveArtifactOptions, _command: Command): Promise<SingleResult<MoveArtifactResult>>;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=move.d.ts.map
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { connectArtifactClient, toArtifactCommandError, } from "./shared.js";
|
|
2
|
+
const moveArtifactSchema = {
|
|
3
|
+
idField: "id",
|
|
4
|
+
columns: [
|
|
5
|
+
{ header: "ID", field: "id", width: 12 },
|
|
6
|
+
{ header: "STORED", field: "storageLocation", width: 14 },
|
|
7
|
+
{ header: "STATUS", field: "status", width: 12 },
|
|
8
|
+
{ header: "UPDATED", field: "updatedAt", width: 24 },
|
|
9
|
+
],
|
|
10
|
+
};
|
|
11
|
+
export async function runMoveCommand(id, options, _command) {
|
|
12
|
+
if (options.to !== "repository" && options.to !== "host") {
|
|
13
|
+
throw {
|
|
14
|
+
code: "INVALID_ARTIFACT_STORAGE_LOCATION",
|
|
15
|
+
message: "--to must be repository or host.",
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
const client = await connectArtifactClient(options.host, "move");
|
|
19
|
+
try {
|
|
20
|
+
const payload = await client.artifactMoveStore({ artifactId: id, destination: options.to });
|
|
21
|
+
if (!payload.success)
|
|
22
|
+
throw new Error(payload.error ?? "Artifact move failed");
|
|
23
|
+
return {
|
|
24
|
+
type: "single",
|
|
25
|
+
data: {
|
|
26
|
+
id: payload.artifact.id,
|
|
27
|
+
storageLocation: payload.artifact.storageLocation ?? null,
|
|
28
|
+
status: payload.artifact.status,
|
|
29
|
+
updatedAt: payload.artifact.updatedAt,
|
|
30
|
+
},
|
|
31
|
+
schema: moveArtifactSchema,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
throw toArtifactCommandError("ARTIFACT_MOVE_FAILED", "move artifact", error);
|
|
36
|
+
}
|
|
37
|
+
finally {
|
|
38
|
+
await client.close().catch(() => { });
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=move.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ArtifactMetadata } from "@otto-code/protocol/artifacts/types";
|
|
2
|
+
import type { Command } from "commander";
|
|
3
|
+
import type { SingleResult } from "../../output/index.js";
|
|
4
|
+
import { type ArtifactCommandOptions } from "./shared.js";
|
|
5
|
+
export interface RegeneratedArtifactResult {
|
|
6
|
+
id: string;
|
|
7
|
+
status: string;
|
|
8
|
+
updatedAt: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function toRegeneratedArtifactResult(artifact: ArtifactMetadata): RegeneratedArtifactResult;
|
|
11
|
+
export declare function runRegenerateCommand(id: string, options: ArtifactCommandOptions, _command: Command): Promise<SingleResult<RegeneratedArtifactResult>>;
|
|
12
|
+
//# sourceMappingURL=regenerate.d.ts.map
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { connectArtifactClient, toArtifactCommandError, } from "./shared.js";
|
|
2
|
+
const regeneratedArtifactSchema = {
|
|
3
|
+
idField: "id",
|
|
4
|
+
columns: [
|
|
5
|
+
{ header: "ID", field: "id", width: 16 },
|
|
6
|
+
{ header: "STATUS", field: "status", width: 12 },
|
|
7
|
+
{ header: "UPDATED", field: "updatedAt", width: 24 },
|
|
8
|
+
],
|
|
9
|
+
};
|
|
10
|
+
export function toRegeneratedArtifactResult(artifact) {
|
|
11
|
+
return { id: artifact.id, status: artifact.status, updatedAt: artifact.updatedAt };
|
|
12
|
+
}
|
|
13
|
+
export async function runRegenerateCommand(id, options, _command) {
|
|
14
|
+
const client = await connectArtifactClient(options.host, "artifacts");
|
|
15
|
+
try {
|
|
16
|
+
const payload = await client.artifactRegenerate({ artifactId: id });
|
|
17
|
+
if (!payload.success)
|
|
18
|
+
throw new Error(payload.error ?? "Artifact regeneration failed");
|
|
19
|
+
return {
|
|
20
|
+
type: "single",
|
|
21
|
+
data: toRegeneratedArtifactResult(payload.artifact),
|
|
22
|
+
schema: regeneratedArtifactSchema,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
throw toArtifactCommandError("ARTIFACT_REGENERATE_FAILED", "regenerate artifact", error);
|
|
27
|
+
}
|
|
28
|
+
finally {
|
|
29
|
+
await client.close().catch(() => { });
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=regenerate.js.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
import type { ArtifactMetadata } from "@otto-code/protocol/artifacts/types";
|
|
3
|
+
import type { SingleResult } from "../../output/index.js";
|
|
4
|
+
import { type ArtifactCommandOptions } from "./shared.js";
|
|
5
|
+
export interface RepairedArtifactResult {
|
|
6
|
+
id: string;
|
|
7
|
+
status: string;
|
|
8
|
+
updatedAt: string;
|
|
9
|
+
repairAvailable: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare function toRepairedArtifactResult(artifact: ArtifactMetadata): RepairedArtifactResult;
|
|
12
|
+
export declare function runRepairCommand(id: string, options: ArtifactCommandOptions, _command: Command): Promise<SingleResult<RepairedArtifactResult>>;
|
|
13
|
+
//# sourceMappingURL=repair.d.ts.map
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { connectArtifactClient, toArtifactCommandError, } from "./shared.js";
|
|
2
|
+
const repairedArtifactSchema = {
|
|
3
|
+
idField: "id",
|
|
4
|
+
columns: [
|
|
5
|
+
{ header: "ID", field: "id", width: 16 },
|
|
6
|
+
{ header: "STATUS", field: "status", width: 12 },
|
|
7
|
+
{ header: "UPDATED", field: "updatedAt", width: 24 },
|
|
8
|
+
{ header: "REPAIR AVAILABLE", field: "repairAvailable", width: 18 },
|
|
9
|
+
],
|
|
10
|
+
};
|
|
11
|
+
export function toRepairedArtifactResult(artifact) {
|
|
12
|
+
return {
|
|
13
|
+
id: artifact.id,
|
|
14
|
+
status: artifact.status,
|
|
15
|
+
updatedAt: artifact.updatedAt,
|
|
16
|
+
repairAvailable: artifact.repairAvailable ?? false,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export async function runRepairCommand(id, options, _command) {
|
|
20
|
+
const client = await connectArtifactClient(options.host, "repair");
|
|
21
|
+
try {
|
|
22
|
+
const payload = await client.artifactRepair({ artifactId: id });
|
|
23
|
+
if (!payload.success)
|
|
24
|
+
throw new Error(payload.error ?? "Artifact repair failed");
|
|
25
|
+
return {
|
|
26
|
+
type: "single",
|
|
27
|
+
data: toRepairedArtifactResult(payload.artifact),
|
|
28
|
+
schema: repairedArtifactSchema,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
throw toArtifactCommandError("ARTIFACT_REPAIR_FAILED", "repair artifact", error);
|
|
33
|
+
}
|
|
34
|
+
finally {
|
|
35
|
+
await client.close().catch(() => { });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=repair.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { DaemonClient } from "@otto-code/client/internal/daemon-client";
|
|
2
|
+
import type { CommandError, CommandOptions } from "../../output/index.js";
|
|
3
|
+
export interface ArtifactCommandOptions extends CommandOptions {
|
|
4
|
+
host?: string;
|
|
5
|
+
}
|
|
6
|
+
type ArtifactCommandCapability = "artifacts" | "data" | "move" | "repair";
|
|
7
|
+
export declare function connectArtifactClient(host?: string, capability?: ArtifactCommandCapability): Promise<DaemonClient>;
|
|
8
|
+
export declare function toArtifactCommandError(code: string, action: string, error: unknown): CommandError;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=shared.d.ts.map
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { connectToDaemon, getDaemonHost } from "../../utils/client.js";
|
|
2
|
+
function supportsArtifactCapability(capability, features) {
|
|
3
|
+
switch (capability) {
|
|
4
|
+
case "artifacts":
|
|
5
|
+
return features?.artifacts;
|
|
6
|
+
case "data":
|
|
7
|
+
return features?.artifactDataUpdate;
|
|
8
|
+
case "move":
|
|
9
|
+
return features?.artifactStoreMove;
|
|
10
|
+
case "repair":
|
|
11
|
+
return features?.artifactRepair;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function artifactCapabilityUpgradeMessage(capability) {
|
|
15
|
+
switch (capability) {
|
|
16
|
+
case "artifacts":
|
|
17
|
+
return "Update the host to list artifacts.";
|
|
18
|
+
case "data":
|
|
19
|
+
return "Update the host to read or update artifact data.";
|
|
20
|
+
case "move":
|
|
21
|
+
return "Update the host to move artifacts between storage locations.";
|
|
22
|
+
case "repair":
|
|
23
|
+
return "Update the host to repair artifact output.";
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export async function connectArtifactClient(host, capability = "data") {
|
|
27
|
+
const daemonHost = getDaemonHost({ host });
|
|
28
|
+
try {
|
|
29
|
+
const client = await connectToDaemon({ host });
|
|
30
|
+
const supportsCapability = supportsArtifactCapability(capability, client.getLastServerInfoMessage()?.features);
|
|
31
|
+
// COMPAT(artifactDataUpdate): added in v0.9.0, remove after 2027-02-28.
|
|
32
|
+
// COMPAT(artifactStoreMove): added in v0.9.0, remove after 2027-02-28.
|
|
33
|
+
// COMPAT(artifactRepair): added in v0.9.0, remove after 2027-02-28.
|
|
34
|
+
if (!supportsCapability) {
|
|
35
|
+
await client.close().catch(() => { });
|
|
36
|
+
throw {
|
|
37
|
+
code: "DAEMON_UPDATE_REQUIRED",
|
|
38
|
+
message: artifactCapabilityUpgradeMessage(capability),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
return client;
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
if (error && typeof error === "object" && "code" in error && "message" in error) {
|
|
45
|
+
throw error;
|
|
46
|
+
}
|
|
47
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
48
|
+
throw {
|
|
49
|
+
code: "DAEMON_NOT_RUNNING",
|
|
50
|
+
message: `Cannot connect to daemon at ${daemonHost}: ${message}`,
|
|
51
|
+
details: "Start the daemon with: otto daemon start",
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export function toArtifactCommandError(code, action, error) {
|
|
56
|
+
if (error && typeof error === "object" && "code" in error && "message" in error) {
|
|
57
|
+
return error;
|
|
58
|
+
}
|
|
59
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
60
|
+
return { code, message: `Failed to ${action}: ${message}` };
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=shared.js.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
import type { SingleResult } from "../../output/index.js";
|
|
3
|
+
import { type ArtifactCommandOptions } from "./shared.js";
|
|
4
|
+
interface UpdateDataOptions extends ArtifactCommandOptions {
|
|
5
|
+
data?: unknown;
|
|
6
|
+
}
|
|
7
|
+
export declare function runUpdateDataCommand(id: string, options: UpdateDataOptions, _command: Command): Promise<SingleResult<{
|
|
8
|
+
id: string;
|
|
9
|
+
status: string;
|
|
10
|
+
updatedAt: string;
|
|
11
|
+
}>>;
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=update-data.d.ts.map
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { connectArtifactClient, toArtifactCommandError, } from "./shared.js";
|
|
2
|
+
const artifactDataUpdateSchema = {
|
|
3
|
+
idField: "id",
|
|
4
|
+
columns: [
|
|
5
|
+
{ header: "ID", field: "id", width: 12 },
|
|
6
|
+
{ header: "STATUS", field: "status", width: 12 },
|
|
7
|
+
{ header: "UPDATED", field: "updatedAt", width: 24 },
|
|
8
|
+
],
|
|
9
|
+
};
|
|
10
|
+
export async function runUpdateDataCommand(id, options, _command) {
|
|
11
|
+
let data;
|
|
12
|
+
try {
|
|
13
|
+
if (typeof options.data !== "string") {
|
|
14
|
+
throw new Error("--data JSON is required");
|
|
15
|
+
}
|
|
16
|
+
data = JSON.parse(options.data);
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
throw toArtifactCommandError("INVALID_ARTIFACT_DATA", "parse --data JSON", error);
|
|
20
|
+
}
|
|
21
|
+
const client = await connectArtifactClient(options.host, "data");
|
|
22
|
+
try {
|
|
23
|
+
const payload = await client.artifactUpdateData({ artifactId: id, data });
|
|
24
|
+
if (!payload.success)
|
|
25
|
+
throw new Error(payload.error ?? "Artifact data update failed");
|
|
26
|
+
return {
|
|
27
|
+
type: "single",
|
|
28
|
+
data: {
|
|
29
|
+
id: payload.artifact.id,
|
|
30
|
+
status: payload.artifact.status,
|
|
31
|
+
updatedAt: payload.artifact.updatedAt,
|
|
32
|
+
},
|
|
33
|
+
schema: artifactDataUpdateSchema,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
throw toArtifactCommandError("ARTIFACT_DATA_UPDATE_FAILED", "update artifact data", error);
|
|
38
|
+
}
|
|
39
|
+
finally {
|
|
40
|
+
await client.close().catch(() => { });
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=update-data.js.map
|
|
@@ -5,8 +5,9 @@ import { runStopCommand } from "./stop.js";
|
|
|
5
5
|
import { runRestartCommand } from "./restart.js";
|
|
6
6
|
import { runSetPasswordCommand } from "./set-password.js";
|
|
7
7
|
import { pairCommand } from "./pair.js";
|
|
8
|
+
import { runDaemonReloadCommand } from "./reload.js";
|
|
8
9
|
import { withOutput } from "../../output/index.js";
|
|
9
|
-
import { addJsonOption } from "../../utils/command-options.js";
|
|
10
|
+
import { addJsonAndDaemonHostOptions, addJsonOption } from "../../utils/command-options.js";
|
|
10
11
|
function resolveHostnamesOption(hostnames, allowedHosts) {
|
|
11
12
|
if (typeof hostnames === "string")
|
|
12
13
|
return hostnames;
|
|
@@ -18,6 +19,7 @@ export function createDaemonCommand() {
|
|
|
18
19
|
const daemon = new Command("daemon").description("Manage the Otto daemon");
|
|
19
20
|
daemon.addCommand(startCommand());
|
|
20
21
|
daemon.addCommand(pairCommand());
|
|
22
|
+
addJsonAndDaemonHostOptions(daemon.command("reload").description("Reload config.json without restarting the daemon")).action(withOutput(runDaemonReloadCommand));
|
|
21
23
|
addJsonOption(daemon.command("status").description("Show local daemon status"))
|
|
22
24
|
.option("--home <path>", "Otto home directory (default: ~/.otto)")
|
|
23
25
|
.action(withOutput(runStatusCommand));
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
import type { CommandOptions, OutputSchema, SingleResult } from "../../output/index.js";
|
|
3
|
+
export interface DaemonReloadResult {
|
|
4
|
+
appliedPaths: string[];
|
|
5
|
+
restartRequiredPaths: string[];
|
|
6
|
+
overrideControlledPaths: string[];
|
|
7
|
+
}
|
|
8
|
+
export declare const daemonReloadSchema: OutputSchema<DaemonReloadResult>;
|
|
9
|
+
export declare function runDaemonReloadCommand(options: CommandOptions, _command: Command): Promise<SingleResult<DaemonReloadResult>>;
|
|
10
|
+
//# sourceMappingURL=reload.d.ts.map
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { connectToDaemon } from "../../utils/client.js";
|
|
2
|
+
export const daemonReloadSchema = {
|
|
3
|
+
idField: () => "daemon-config",
|
|
4
|
+
columns: [],
|
|
5
|
+
renderHuman(result) {
|
|
6
|
+
if (result.type !== "single")
|
|
7
|
+
return "";
|
|
8
|
+
const lines = ["Configuration reloaded."];
|
|
9
|
+
if (result.data.restartRequiredPaths.length > 0) {
|
|
10
|
+
lines.push("", "Warning: These changes require a daemon restart:", ...result.data.restartRequiredPaths.map((path) => ` ${path}`), "", "Run: otto daemon restart");
|
|
11
|
+
}
|
|
12
|
+
if (result.data.overrideControlledPaths.length > 0) {
|
|
13
|
+
lines.push("", "Warning: These settings are controlled by daemon launch overrides:", ...result.data.overrideControlledPaths.map((path) => ` ${path}`));
|
|
14
|
+
}
|
|
15
|
+
return lines.join("\n");
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
export async function runDaemonReloadCommand(options, _command) {
|
|
19
|
+
const client = await connectToDaemon({ host: options.host });
|
|
20
|
+
try {
|
|
21
|
+
const payload = await client.reloadDaemonConfig();
|
|
22
|
+
return {
|
|
23
|
+
type: "single",
|
|
24
|
+
data: {
|
|
25
|
+
appliedPaths: payload.appliedPaths,
|
|
26
|
+
restartRequiredPaths: payload.restartRequiredPaths,
|
|
27
|
+
overrideControlledPaths: payload.overrideControlledPaths,
|
|
28
|
+
},
|
|
29
|
+
schema: daemonReloadSchema,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
finally {
|
|
33
|
+
await client.close();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=reload.js.map
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ProviderSnapshotEntry } from "@otto-code/protocol/agent-types";
|
|
1
2
|
export interface HubStatus {
|
|
2
3
|
state: string;
|
|
3
4
|
daemonId: string | null;
|
|
@@ -6,6 +7,9 @@ export interface HubStatus {
|
|
|
6
7
|
connectedAt: string | null;
|
|
7
8
|
lastError: string | null;
|
|
8
9
|
}
|
|
10
|
+
export interface HubProvidersSnapshotOptions {
|
|
11
|
+
cwd?: string;
|
|
12
|
+
}
|
|
9
13
|
export interface HubDaemonClient {
|
|
10
14
|
connectHub(url: string, token: string): Promise<{
|
|
11
15
|
status: HubStatus;
|
|
@@ -17,6 +21,9 @@ export interface HubDaemonClient {
|
|
|
17
21
|
status: HubStatus;
|
|
18
22
|
warning?: string;
|
|
19
23
|
}>;
|
|
24
|
+
getProvidersSnapshot(options?: HubProvidersSnapshotOptions): Promise<{
|
|
25
|
+
entries: ProviderSnapshotEntry[];
|
|
26
|
+
}>;
|
|
20
27
|
close(): Promise<void>;
|
|
21
28
|
}
|
|
22
29
|
export interface HubDaemonConnection {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { HubBundleFile } from "../deploy-bundle.js";
|
|
2
|
-
import { type CliAuthorization, type CliAuthorizationPoll, type HubInstallResult, type HubConfigurationResources, type HubProject, type HubValidationResult } from "./internal/contracts.js";
|
|
3
|
-
export type { CliAuthorization, CliAuthorizationPoll, HubInstallResult, HubConfigurationResources, HubProject, HubValidationResult, } from "./internal/contracts.js";
|
|
2
|
+
import { type CliAuthorization, type CliAuthorizationPoll, type HubInstallResult, type HubConfigurationResources, type HubSetupResources, type HubProject, type HubValidationResult } from "./internal/contracts.js";
|
|
3
|
+
export type { CliAuthorization, CliAuthorizationPoll, HubInstallResult, HubConfigurationResources, HubSetupResources, HubProject, HubValidationResult, } from "./internal/contracts.js";
|
|
4
4
|
interface HubConfigurationInput {
|
|
5
5
|
origin: string;
|
|
6
6
|
apiKey: string;
|
|
@@ -12,6 +12,7 @@ export declare class HubHttpClient {
|
|
|
12
12
|
pollCliAuthorization(origin: string, deviceCode: string, timeoutMilliseconds: number): Promise<CliAuthorizationPoll>;
|
|
13
13
|
listProjects(origin: string, apiKey: string): Promise<HubProject[]>;
|
|
14
14
|
listConfigurationResources(origin: string, apiKey: string): Promise<HubConfigurationResources>;
|
|
15
|
+
listSetupResources(origin: string, apiKey: string): Promise<HubSetupResources>;
|
|
15
16
|
installConfiguration(input: HubConfigurationInput): Promise<HubInstallResult>;
|
|
16
17
|
validateConfiguration(input: HubConfigurationInput): Promise<HubValidationResult>;
|
|
17
18
|
issueEnrollmentToken(origin: string, apiKey: string): Promise<string>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { HubCommandError } from "../error.js";
|
|
2
|
-
import { authorizationPollSchema, authorizationSchema, configurationResourcesSchema, enrollmentTokenSchema, installResponseSchema, projectsResponseSchema, validationResponseSchema, } from "./internal/contracts.js";
|
|
2
|
+
import { authorizationPollSchema, authorizationSchema, configurationResourcesSchema, enrollmentTokenSchema, installResponseSchema, projectsResponseSchema, validationResponseSchema, setupResourcesSchema, } from "./internal/contracts.js";
|
|
3
3
|
import { requestHub } from "./internal/transport.js";
|
|
4
4
|
export class HubHttpClient {
|
|
5
5
|
startCliAuthorization(origin) {
|
|
@@ -56,6 +56,17 @@ export class HubHttpClient {
|
|
|
56
56
|
failureMessage: "Hub configuration resource listing failed",
|
|
57
57
|
});
|
|
58
58
|
}
|
|
59
|
+
listSetupResources(origin, apiKey) {
|
|
60
|
+
return requestHub({
|
|
61
|
+
origin,
|
|
62
|
+
path: "/api/v1/setup-resources",
|
|
63
|
+
method: "GET",
|
|
64
|
+
apiKey,
|
|
65
|
+
successStatus: 200,
|
|
66
|
+
schema: setupResourcesSchema,
|
|
67
|
+
failureMessage: "Hub guided setup resource listing failed",
|
|
68
|
+
});
|
|
69
|
+
}
|
|
59
70
|
installConfiguration(input) {
|
|
60
71
|
return requestHub({
|
|
61
72
|
origin: input.origin,
|
|
@@ -62,6 +62,22 @@ export declare const configurationResourcesSchema: z.ZodObject<{
|
|
|
62
62
|
teamName: z.ZodString;
|
|
63
63
|
}, z.core.$strict>>;
|
|
64
64
|
}, z.core.$strict>;
|
|
65
|
+
export declare const setupResourcesSchema: z.ZodObject<{
|
|
66
|
+
github: z.ZodArray<z.ZodObject<{
|
|
67
|
+
slug: z.ZodString;
|
|
68
|
+
accountLogin: z.ZodString;
|
|
69
|
+
accountType: z.ZodString;
|
|
70
|
+
repositories: z.ZodArray<z.ZodString>;
|
|
71
|
+
}, z.core.$strict>>;
|
|
72
|
+
discord: z.ZodArray<z.ZodObject<{
|
|
73
|
+
guildId: z.ZodString;
|
|
74
|
+
guildName: z.ZodString;
|
|
75
|
+
}, z.core.$strict>>;
|
|
76
|
+
slack: z.ZodArray<z.ZodObject<{
|
|
77
|
+
teamId: z.ZodString;
|
|
78
|
+
teamName: z.ZodString;
|
|
79
|
+
}, z.core.$strict>>;
|
|
80
|
+
}, z.core.$strict>;
|
|
65
81
|
export declare const installResponseSchema: z.ZodObject<{
|
|
66
82
|
projectSlug: z.ZodString;
|
|
67
83
|
version: z.ZodNumber;
|
|
@@ -80,6 +96,7 @@ export type CliAuthorization = z.infer<typeof authorizationSchema>;
|
|
|
80
96
|
export type CliAuthorizationPoll = z.infer<typeof authorizationPollSchema>;
|
|
81
97
|
export type HubProject = z.infer<typeof projectSchema>;
|
|
82
98
|
export type HubConfigurationResources = z.infer<typeof configurationResourcesSchema>;
|
|
99
|
+
export type HubSetupResources = z.infer<typeof setupResourcesSchema>;
|
|
83
100
|
export type HubInstallResult = z.infer<typeof installResponseSchema>;
|
|
84
101
|
export type HubValidationResult = z.infer<typeof validationResponseSchema>;
|
|
85
102
|
export {};
|
|
@@ -45,6 +45,20 @@ export const configurationResourcesSchema = z
|
|
|
45
45
|
slack: z.array(z.object({ slug: z.string().min(1), teamName: z.string().min(1) }).strict()),
|
|
46
46
|
})
|
|
47
47
|
.strict();
|
|
48
|
+
export const setupResourcesSchema = z
|
|
49
|
+
.object({
|
|
50
|
+
github: z.array(z
|
|
51
|
+
.object({
|
|
52
|
+
slug: z.string().min(1),
|
|
53
|
+
accountLogin: z.string().min(1),
|
|
54
|
+
accountType: z.string().min(1),
|
|
55
|
+
repositories: z.array(z.string().min(1)),
|
|
56
|
+
})
|
|
57
|
+
.strict()),
|
|
58
|
+
discord: z.array(z.object({ guildId: z.string().min(1), guildName: z.string().min(1) }).strict()),
|
|
59
|
+
slack: z.array(z.object({ teamId: z.string().min(1), teamName: z.string().min(1) }).strict()),
|
|
60
|
+
})
|
|
61
|
+
.strict();
|
|
48
62
|
export const installResponseSchema = z
|
|
49
63
|
.object({
|
|
50
64
|
projectSlug: z.string().min(1),
|
|
@@ -14,7 +14,7 @@ import { addHubProjectsCommand } from "./projects.js";
|
|
|
14
14
|
import { processHubReporter } from "./reporter.js";
|
|
15
15
|
import { hubStatusResult } from "./status-output.js";
|
|
16
16
|
import { addHubResolutionHelp } from "./help.js";
|
|
17
|
-
import { addHubInitCommand } from "./init.js";
|
|
17
|
+
import { addHubInitCommand, continueHubGuidedSetup } from "./init.js";
|
|
18
18
|
function productionEnvironment() {
|
|
19
19
|
const env = process.env;
|
|
20
20
|
const hub = new HubHttpClient();
|
|
@@ -37,6 +37,8 @@ export function createHubCommand(overrides = {}) {
|
|
|
37
37
|
credentials: environment.credentials,
|
|
38
38
|
flow: environment.login,
|
|
39
39
|
reporter: environment.reporter,
|
|
40
|
+
isInteractive: environment.isInteractive,
|
|
41
|
+
continueGuidedSetup: (origin) => continueHubGuidedSetup(origin, environment),
|
|
40
42
|
});
|
|
41
43
|
addHubInitCommand(hub, environment);
|
|
42
44
|
addHubConnectCommand(hub, {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { HubDeployBundle } from "./deploy-bundle.js";
|
|
2
2
|
import type { HubProject } from "./hub-client/index.js";
|
|
3
3
|
import type { HubStatus } from "./daemon-client.js";
|
|
4
|
+
import type { HubStarterAgentRuntime } from "./starter-agent-runtime.js";
|
|
4
5
|
export type HubInitProvider = "github" | "slack" | "discord";
|
|
5
6
|
export type HubInitProjectResolution = {
|
|
6
7
|
kind: "none";
|
|
@@ -30,6 +31,7 @@ export interface HubInitOpeningPlan {
|
|
|
30
31
|
export interface HubInitScaffoldInput {
|
|
31
32
|
cwd: string;
|
|
32
33
|
daemonSlug: string;
|
|
34
|
+
agent: HubStarterAgentRuntime;
|
|
33
35
|
provider: HubInitProvider;
|
|
34
36
|
providerFilters: Readonly<Record<string, string>>;
|
|
35
37
|
}
|
|
@@ -39,6 +41,7 @@ export interface HubInitScaffold {
|
|
|
39
41
|
workflow: string;
|
|
40
42
|
testAction: string;
|
|
41
43
|
}
|
|
44
|
+
export declare function hubLoginResumeCommand(step: "connect" | "init", origin: string): string;
|
|
42
45
|
export declare function planHubInitOpening(input: {
|
|
43
46
|
loggedIn: boolean;
|
|
44
47
|
ottoDirectoryExists: boolean;
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import YAML from "yaml";
|
|
3
|
+
export function hubLoginResumeCommand(step, origin) {
|
|
4
|
+
return step === "connect" ? `otto hub connect ${origin}` : "otto hub init";
|
|
5
|
+
}
|
|
3
6
|
export function planHubInitOpening(input) {
|
|
4
7
|
return {
|
|
5
8
|
replaceExisting: input.ottoDirectoryExists,
|
|
@@ -47,9 +50,10 @@ export function createHubInitScaffold(input) {
|
|
|
47
50
|
},
|
|
48
51
|
},
|
|
49
52
|
agents: {
|
|
50
|
-
|
|
51
|
-
provider:
|
|
52
|
-
|
|
53
|
+
starter: {
|
|
54
|
+
provider: input.agent.provider,
|
|
55
|
+
model: input.agent.model,
|
|
56
|
+
...(input.agent.mode === undefined ? {} : { mode: input.agent.mode }),
|
|
53
57
|
},
|
|
54
58
|
},
|
|
55
59
|
}, { lineWidth: 0 });
|
|
@@ -114,7 +118,7 @@ function workflow(input) {
|
|
|
114
118
|
environment: input.environment,
|
|
115
119
|
max_runtime: "90m",
|
|
116
120
|
idle_timeout: "10m",
|
|
117
|
-
agent: "
|
|
121
|
+
agent: "starter",
|
|
118
122
|
prompt: [
|
|
119
123
|
{
|
|
120
124
|
text: `${replyInstruction}complete this request and call hub.finish_execution when done.\n\n<user-prompt>\n\${{ otto.prompt }}\n</user-prompt>\n`,
|