@stigmer/cli 3.14.1 → 3.15.1
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/client/client.d.ts +20 -1
- package/client/client.d.ts.map +1 -1
- package/client/client.js +32 -5
- package/client/client.js.map +1 -1
- package/commands/auth/index.d.ts.map +1 -1
- package/commands/auth/index.js +7 -36
- package/commands/auth/index.js.map +1 -1
- package/commands/auth/login.d.ts +13 -0
- package/commands/auth/login.d.ts.map +1 -0
- package/commands/auth/login.js +58 -0
- package/commands/auth/login.js.map +1 -0
- package/commands/auth/whoami.d.ts +18 -0
- package/commands/auth/whoami.d.ts.map +1 -0
- package/commands/auth/whoami.js +58 -0
- package/commands/auth/whoami.js.map +1 -0
- package/commands/draft.d.ts.map +1 -1
- package/commands/draft.js +24 -8
- package/commands/draft.js.map +1 -1
- package/commands/internal-daemon.d.ts.map +1 -1
- package/commands/internal-daemon.js +2 -9
- package/commands/internal-daemon.js.map +1 -1
- package/commands/run.d.ts.map +1 -1
- package/commands/run.js +15 -6
- package/commands/run.js.map +1 -1
- package/commands/up.d.ts.map +1 -1
- package/commands/up.js +37 -8
- package/commands/up.js.map +1 -1
- package/local/artifact.d.ts +13 -3
- package/local/artifact.d.ts.map +1 -1
- package/local/artifact.js +31 -10
- package/local/artifact.js.map +1 -1
- package/local/daemon/components.d.ts.map +1 -1
- package/local/daemon/components.js +2 -0
- package/local/daemon/components.js.map +1 -1
- package/local/daemon/host.d.ts +8 -0
- package/local/daemon/host.d.ts.map +1 -1
- package/local/daemon/host.js +42 -1
- package/local/daemon/host.js.map +1 -1
- package/local/daemon/index.d.ts +3 -3
- package/local/daemon/index.d.ts.map +1 -1
- package/local/daemon/index.js +2 -2
- package/local/daemon/index.js.map +1 -1
- package/local/daemon/launch.d.ts +29 -1
- package/local/daemon/launch.d.ts.map +1 -1
- package/local/daemon/launch.js +73 -23
- package/local/daemon/launch.js.map +1 -1
- package/local/daemon/process.d.ts +16 -2
- package/local/daemon/process.d.ts.map +1 -1
- package/local/daemon/process.js +16 -2
- package/local/daemon/process.js.map +1 -1
- package/local/daemon/types.d.ts +2 -0
- package/local/daemon/types.d.ts.map +1 -1
- package/local/paths.d.ts +14 -4
- package/local/paths.d.ts.map +1 -1
- package/local/paths.js +17 -4
- package/local/paths.js.map +1 -1
- package/local/runtime/runner.d.ts +4 -0
- package/local/runtime/runner.d.ts.map +1 -1
- package/local/runtime/runner.js +10 -6
- package/local/runtime/runner.js.map +1 -1
- package/local/runtime/server.d.ts +5 -0
- package/local/runtime/server.d.ts.map +1 -1
- package/local/runtime/server.js +13 -8
- package/local/runtime/server.js.map +1 -1
- package/local/seedpack/content.d.ts +4 -0
- package/local/seedpack/content.d.ts.map +1 -1
- package/local/seedpack/content.js +11 -11
- package/local/seedpack/content.js.map +1 -1
- package/local/state/lock.d.ts +3 -2
- package/local/state/lock.d.ts.map +1 -1
- package/local/state/lock.js +23 -9
- package/local/state/lock.js.map +1 -1
- package/local/state/proc.d.ts +13 -0
- package/local/state/proc.d.ts.map +1 -1
- package/local/state/proc.js +15 -0
- package/local/state/proc.js.map +1 -1
- package/local/temporal/download.d.ts +7 -1
- package/local/temporal/download.d.ts.map +1 -1
- package/local/temporal/download.js +21 -11
- package/local/temporal/download.js.map +1 -1
- package/local/temporal/index.d.ts +3 -3
- package/local/temporal/index.d.ts.map +1 -1
- package/local/temporal/index.js +2 -2
- package/local/temporal/index.js.map +1 -1
- package/local/temporal/inspect.d.ts +10 -3
- package/local/temporal/inspect.d.ts.map +1 -1
- package/local/temporal/inspect.js +52 -13
- package/local/temporal/inspect.js.map +1 -1
- package/local/temporal/manager.d.ts +16 -4
- package/local/temporal/manager.d.ts.map +1 -1
- package/local/temporal/manager.js +19 -6
- package/local/temporal/manager.js.map +1 -1
- package/package.json +5 -5
- package/resources/run/prepare.d.ts +10 -8
- package/resources/run/prepare.d.ts.map +1 -1
- package/resources/run/prepare.js +18 -4
- package/resources/run/prepare.js.map +1 -1
- package/src/client/client.test.ts +89 -1
- package/src/client/client.ts +82 -9
- package/src/commands/auth/index.ts +7 -56
- package/src/commands/auth/login.test.ts +52 -0
- package/src/commands/auth/login.ts +70 -0
- package/src/commands/auth/whoami.test.ts +96 -0
- package/src/commands/auth/whoami.ts +87 -0
- package/src/commands/draft.ts +85 -26
- package/src/commands/internal-daemon.ts +2 -10
- package/src/commands/run.ts +77 -35
- package/src/commands/up.ts +52 -8
- package/src/local/artifact.test.ts +27 -1
- package/src/local/artifact.ts +35 -13
- package/src/local/daemon/components.ts +2 -0
- package/src/local/daemon/daemon.integration.test.ts +33 -0
- package/src/local/daemon/host.ts +50 -1
- package/src/local/daemon/index.ts +3 -3
- package/src/local/daemon/launch.test.ts +136 -0
- package/src/local/daemon/launch.ts +108 -27
- package/src/local/daemon/process.ts +31 -4
- package/src/local/daemon/supervisor.test.ts +1 -1
- package/src/local/daemon/types.ts +2 -0
- package/src/local/paths.ts +17 -4
- package/src/local/runtime/runner.ts +10 -6
- package/src/local/runtime/runtime.test.ts +15 -0
- package/src/local/runtime/server.test.ts +37 -0
- package/src/local/runtime/server.ts +17 -12
- package/src/local/seedpack/content.test.ts +21 -0
- package/src/local/seedpack/content.ts +15 -16
- package/src/local/state/lock.ts +24 -9
- package/src/local/state/proc.ts +16 -0
- package/src/local/state/state.test.ts +25 -1
- package/src/local/temporal/download.test.ts +79 -11
- package/src/local/temporal/download.ts +24 -11
- package/src/local/temporal/index.ts +11 -3
- package/src/local/temporal/inspect.test.ts +83 -0
- package/src/local/temporal/inspect.ts +60 -12
- package/src/local/temporal/manager.test.ts +34 -1
- package/src/local/temporal/manager.ts +22 -8
- package/src/resources/run/prepare.test.ts +28 -26
- package/src/resources/run/prepare.ts +48 -18
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// Unit tests for `stigmer auth whoami`'s result (commands/auth/whoami.ts;
|
|
2
|
+
// 20260911.11 A3): the command runs the SDK's ensureMyIdentityAccount — the
|
|
3
|
+
// same first-sign-in flow the console runs — and RENDERS what it learned.
|
|
4
|
+
// whoamiResult is the pure half: given the account and whether this call
|
|
5
|
+
// created it, the CommandResult a person reads. A first sign-in says so
|
|
6
|
+
// (visibility of system status); a missing organization is a hint, not a
|
|
7
|
+
// failure; an empty profile (the unconfigured laptop's operator) renders
|
|
8
|
+
// only the fields it has. Accounts are built with the generated schema
|
|
9
|
+
// (DD-007: the generated type is the contract; refinement 12, slice 4).
|
|
10
|
+
|
|
11
|
+
import { create } from "@bufbuild/protobuf";
|
|
12
|
+
import { IdentityAccountSchema } from "@stigmer/protos/ai/stigmer/iam/identityaccount/v1/api_pb";
|
|
13
|
+
import { describe, expect, it } from "vitest";
|
|
14
|
+
|
|
15
|
+
import { whoamiResult } from "./whoami.js";
|
|
16
|
+
|
|
17
|
+
const ALICE = {
|
|
18
|
+
metadata: { id: "ida_wtr3jcf281yfk9xx61kj59fsme", name: "alice@example.com" },
|
|
19
|
+
spec: {
|
|
20
|
+
idpId: "auth0|alice",
|
|
21
|
+
email: "alice@example.com",
|
|
22
|
+
firstName: "Alice",
|
|
23
|
+
lastName: "Liddell",
|
|
24
|
+
isMachineAccount: false,
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
const ACCOUNT = create(IdentityAccountSchema, ALICE);
|
|
28
|
+
|
|
29
|
+
function fields(
|
|
30
|
+
result: ReturnType<typeof whoamiResult>,
|
|
31
|
+
): Record<string, string> {
|
|
32
|
+
return Object.fromEntries(
|
|
33
|
+
result.sections.flatMap((s) => s.fields.map((f) => [f.key, f.value])),
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
describe("whoamiResult", () => {
|
|
38
|
+
it("renders an existing account as 'Authenticated' with its identity fields", () => {
|
|
39
|
+
const result = whoamiResult(ACCOUNT, { created: false, org: "acme" });
|
|
40
|
+
expect(result.status).toBe("success");
|
|
41
|
+
expect(result.message).toBe("Authenticated");
|
|
42
|
+
expect(fields(result)).toEqual({
|
|
43
|
+
"Account ID": "ida_wtr3jcf281yfk9xx61kj59fsme",
|
|
44
|
+
Name: "alice@example.com",
|
|
45
|
+
Email: "alice@example.com",
|
|
46
|
+
"Full Name": "Alice Liddell",
|
|
47
|
+
"Account Type": "User Account",
|
|
48
|
+
Organization: "acme",
|
|
49
|
+
});
|
|
50
|
+
expect(result.hints).toEqual([]);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("says so when this call created the account — the first sign-in is visible, never silent", () => {
|
|
54
|
+
const result = whoamiResult(ACCOUNT, { created: true, org: "acme" });
|
|
55
|
+
expect(result.status).toBe("success");
|
|
56
|
+
expect(result.message).toBe(
|
|
57
|
+
"Authenticated — your account was created on this first sign-in",
|
|
58
|
+
);
|
|
59
|
+
expect(fields(result)["Account ID"]).toBe("ida_wtr3jcf281yfk9xx61kj59fsme");
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("a missing organization is a hint with the command that sets it", () => {
|
|
63
|
+
const result = whoamiResult(ACCOUNT, { created: false, org: "" });
|
|
64
|
+
expect(fields(result).Organization).toBeUndefined();
|
|
65
|
+
expect(result.hints).toEqual([
|
|
66
|
+
"No organization set. Use: stigmer config context set --org <slug>",
|
|
67
|
+
]);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("renders only the fields an empty profile has (the unconfigured laptop's operator)", () => {
|
|
71
|
+
const result = whoamiResult(
|
|
72
|
+
create(IdentityAccountSchema, {
|
|
73
|
+
metadata: { id: "ida_fn0zdvkkkhhrb4wry43zba8gnn", name: "system" },
|
|
74
|
+
spec: { idpId: "local|system" },
|
|
75
|
+
}),
|
|
76
|
+
{ created: false, org: "local" },
|
|
77
|
+
);
|
|
78
|
+
expect(fields(result)).toEqual({
|
|
79
|
+
"Account ID": "ida_fn0zdvkkkhhrb4wry43zba8gnn",
|
|
80
|
+
Name: "system",
|
|
81
|
+
"Account Type": "User Account",
|
|
82
|
+
Organization: "local",
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("names a machine account", () => {
|
|
87
|
+
const result = whoamiResult(
|
|
88
|
+
create(IdentityAccountSchema, {
|
|
89
|
+
...ALICE,
|
|
90
|
+
spec: { ...ALICE.spec, isMachineAccount: true },
|
|
91
|
+
}),
|
|
92
|
+
{ created: false, org: "acme" },
|
|
93
|
+
);
|
|
94
|
+
expect(fields(result)["Account Type"]).toBe("Machine Account");
|
|
95
|
+
});
|
|
96
|
+
});
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// `stigmer auth whoami` — who the connected server says you are.
|
|
2
|
+
//
|
|
3
|
+
// Runs the SDK's ensureMyIdentityAccount, the same first-sign-in flow the
|
|
4
|
+
// console's gate runs (20260911.11 A3): an authenticated caller with no
|
|
5
|
+
// account yet is provisioned here rather than told "not found", so a person
|
|
6
|
+
// who only ever uses the CLI is never left without an account. The result
|
|
7
|
+
// says when THIS call created it — a first sign-in is visible, never silent.
|
|
8
|
+
//
|
|
9
|
+
// whoamiResult is the pure half (given the account and what the call learned,
|
|
10
|
+
// the CommandResult a person reads); runWhoami is the I/O half.
|
|
11
|
+
|
|
12
|
+
import type { IdentityAccount } from "@stigmer/protos/ai/stigmer/iam/identityaccount/v1/api_pb";
|
|
13
|
+
import { ensureMyIdentityAccount } from "@stigmer/sdk";
|
|
14
|
+
import {
|
|
15
|
+
ensureAuthenticated,
|
|
16
|
+
resolveContextOrganization,
|
|
17
|
+
} from "../../config/index.js";
|
|
18
|
+
import { CommandResult } from "../../output/index.js";
|
|
19
|
+
|
|
20
|
+
/** What the call learned beyond the account itself. */
|
|
21
|
+
export interface WhoamiContext {
|
|
22
|
+
/** `true` when this call created the account (a first sign-in). */
|
|
23
|
+
readonly created: boolean;
|
|
24
|
+
/** The organization the CLI context resolves to; "" when none is set. */
|
|
25
|
+
readonly org: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Render the caller's account. Every field is optional on the wire and an
|
|
30
|
+
* empty profile (the unconfigured laptop's operator) renders only what it
|
|
31
|
+
* has; a missing organization is a hint with the command that sets it, not a
|
|
32
|
+
* failure.
|
|
33
|
+
*/
|
|
34
|
+
export function whoamiResult(
|
|
35
|
+
account: IdentityAccount,
|
|
36
|
+
context: WhoamiContext,
|
|
37
|
+
): CommandResult {
|
|
38
|
+
const result = CommandResult.success(
|
|
39
|
+
context.created
|
|
40
|
+
? "Authenticated — your account was created on this first sign-in"
|
|
41
|
+
: "Authenticated",
|
|
42
|
+
);
|
|
43
|
+
const section = result.addSection("");
|
|
44
|
+
|
|
45
|
+
if (account.metadata !== undefined) {
|
|
46
|
+
section.field("Account ID", account.metadata.id);
|
|
47
|
+
if (account.metadata.name !== "")
|
|
48
|
+
section.field("Name", account.metadata.name);
|
|
49
|
+
}
|
|
50
|
+
if (account.spec !== undefined) {
|
|
51
|
+
if (account.spec.email !== "") section.field("Email", account.spec.email);
|
|
52
|
+
if (account.spec.firstName !== "" || account.spec.lastName !== "") {
|
|
53
|
+
section.field(
|
|
54
|
+
"Full Name",
|
|
55
|
+
`${account.spec.firstName} ${account.spec.lastName}`.trim(),
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
section.field(
|
|
59
|
+
"Account Type",
|
|
60
|
+
account.spec.isMachineAccount ? "Machine Account" : "User Account",
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (context.org !== "") {
|
|
65
|
+
section.field("Organization", context.org);
|
|
66
|
+
} else {
|
|
67
|
+
result.hint(
|
|
68
|
+
"No organization set. Use: stigmer config context set --org <slug>",
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return result;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export async function runWhoami(): Promise<CommandResult> {
|
|
76
|
+
const { connectBackend } = await import("../../backend.js");
|
|
77
|
+
const client = connectBackend();
|
|
78
|
+
ensureAuthenticated(client.config);
|
|
79
|
+
|
|
80
|
+
const { account, created } = await ensureMyIdentityAccount(client.stigmer, {
|
|
81
|
+
onProvisioning: () => process.stderr.write("Setting up your account...\n"),
|
|
82
|
+
});
|
|
83
|
+
return whoamiResult(account, {
|
|
84
|
+
created,
|
|
85
|
+
org: resolveContextOrganization(client.config),
|
|
86
|
+
});
|
|
87
|
+
}
|
package/src/commands/draft.ts
CHANGED
|
@@ -8,9 +8,14 @@
|
|
|
8
8
|
// unless detached or a local workspace is in play.
|
|
9
9
|
|
|
10
10
|
import type { Command } from "commander";
|
|
11
|
-
import {
|
|
11
|
+
import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
|
|
12
|
+
import { ensureAuthenticated, resolveOrganization } from "../config/index.js";
|
|
12
13
|
import { UsageError } from "../errors/index.js";
|
|
13
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
addAgentExecFlags,
|
|
16
|
+
type AgentExecOptions,
|
|
17
|
+
toAgentExecFlags,
|
|
18
|
+
} from "./agent-exec-flags.js";
|
|
14
19
|
import { globalOrg } from "./shared.js";
|
|
15
20
|
|
|
16
21
|
// The org that owns the system agent blueprints (installed by the seedpack),
|
|
@@ -31,9 +36,21 @@ interface DraftConfig {
|
|
|
31
36
|
}
|
|
32
37
|
|
|
33
38
|
const CONFIGS: readonly DraftConfig[] = [
|
|
34
|
-
{
|
|
35
|
-
|
|
36
|
-
|
|
39
|
+
{
|
|
40
|
+
name: "skill",
|
|
41
|
+
resourceType: "Skill",
|
|
42
|
+
summary: "create a skill with AI assistance",
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: "agent",
|
|
46
|
+
resourceType: "Agent",
|
|
47
|
+
summary: "create an agent with AI assistance",
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: "mcp-server",
|
|
51
|
+
resourceType: "McpServer",
|
|
52
|
+
summary: "create an MCP server config with AI assistance",
|
|
53
|
+
},
|
|
37
54
|
];
|
|
38
55
|
|
|
39
56
|
// System agent slug per subcommand (Go's draftConfig.AgentName: "<type>-creator").
|
|
@@ -42,17 +59,28 @@ function agentSlug(name: string): string {
|
|
|
42
59
|
}
|
|
43
60
|
|
|
44
61
|
export function registerDraft(program: Command): void {
|
|
45
|
-
const draft = program
|
|
62
|
+
const draft = program
|
|
63
|
+
.command("draft")
|
|
64
|
+
.description("create resource configurations with AI assistance");
|
|
46
65
|
for (const config of CONFIGS) {
|
|
47
66
|
const sub = draft.command(config.name).description(config.summary);
|
|
48
67
|
addAgentExecFlags(sub, true)
|
|
49
|
-
.option(
|
|
68
|
+
.option(
|
|
69
|
+
"-o, --output <dir>",
|
|
70
|
+
"directory to save generated artifacts (default: current directory)",
|
|
71
|
+
)
|
|
50
72
|
.option("--json", "stream events as newline-delimited JSON")
|
|
51
|
-
.action((options: DraftFlags, command: Command) =>
|
|
73
|
+
.action((options: DraftFlags, command: Command) =>
|
|
74
|
+
runDraft(config, options, command),
|
|
75
|
+
);
|
|
52
76
|
}
|
|
53
77
|
}
|
|
54
78
|
|
|
55
|
-
async function runDraft(
|
|
79
|
+
async function runDraft(
|
|
80
|
+
config: DraftConfig,
|
|
81
|
+
options: DraftFlags,
|
|
82
|
+
command: Command,
|
|
83
|
+
): Promise<void> {
|
|
56
84
|
const { connectBackend } = await import("../backend.js");
|
|
57
85
|
const client = connectBackend();
|
|
58
86
|
ensureAuthenticated(client.config);
|
|
@@ -63,29 +91,52 @@ async function runDraft(config: DraftConfig, options: DraftFlags, command: Comma
|
|
|
63
91
|
);
|
|
64
92
|
}
|
|
65
93
|
|
|
66
|
-
const [
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
94
|
+
const [
|
|
95
|
+
{ prepareAgentExec },
|
|
96
|
+
{ executeResolvedAgent },
|
|
97
|
+
{ resolveAgentRef },
|
|
98
|
+
{ localWorkspaceRoots },
|
|
99
|
+
] = await Promise.all([
|
|
100
|
+
import("../resources/run/prepare.js"),
|
|
101
|
+
import("../resources/run/agent-exec.js"),
|
|
102
|
+
import("../resources/run/resolve.js"),
|
|
103
|
+
import("../resources/run/workspace.js"),
|
|
104
|
+
]);
|
|
105
|
+
|
|
106
|
+
const agent = await resolveDraftAgent(
|
|
107
|
+
resolveAgentRef,
|
|
108
|
+
client.stigmer,
|
|
109
|
+
config,
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
const prepared = await prepareAgentExec(
|
|
113
|
+
toAgentExecFlags(options),
|
|
114
|
+
client.stigmer,
|
|
115
|
+
org,
|
|
116
|
+
stderrProgress(),
|
|
117
|
+
{
|
|
118
|
+
// The kind's tier against the server's reported edition, as `run` asks it.
|
|
119
|
+
accountPreferencesAvailable: await client.isResourceAvailable(
|
|
120
|
+
ApiResourceKind.identity_account,
|
|
121
|
+
),
|
|
122
|
+
},
|
|
123
|
+
);
|
|
79
124
|
|
|
80
125
|
if (prepared.attachments.length + prepared.workspaceFileRefs.length > 0) {
|
|
81
|
-
process.stderr.write(
|
|
126
|
+
process.stderr.write(
|
|
127
|
+
`Attached ${prepared.attachments.length + prepared.workspaceFileRefs.length} file(s) as context\n`,
|
|
128
|
+
);
|
|
82
129
|
}
|
|
83
130
|
|
|
84
131
|
await executeResolvedAgent({
|
|
85
132
|
agent,
|
|
86
133
|
prepared,
|
|
87
134
|
org,
|
|
88
|
-
downloadDir: resolveDownloadDir(
|
|
135
|
+
downloadDir: resolveDownloadDir(
|
|
136
|
+
options.output ?? "",
|
|
137
|
+
prepared.detach,
|
|
138
|
+
localWorkspaceRoots(prepared.workspaceEntries),
|
|
139
|
+
),
|
|
89
140
|
outputMode: options.json === true ? "json" : "inline",
|
|
90
141
|
client,
|
|
91
142
|
});
|
|
@@ -100,7 +151,11 @@ async function resolveDraftAgent(
|
|
|
100
151
|
): Promise<import("@stigmer/protos/ai/stigmer/agentic/agent/v1/api_pb").Agent> {
|
|
101
152
|
const slug = agentSlug(config.name);
|
|
102
153
|
try {
|
|
103
|
-
return await resolveAgentRef(
|
|
154
|
+
return await resolveAgentRef(
|
|
155
|
+
client,
|
|
156
|
+
`${SYSTEM_AGENT_ORG}/${slug}`,
|
|
157
|
+
SYSTEM_AGENT_ORG,
|
|
158
|
+
);
|
|
104
159
|
} catch {
|
|
105
160
|
throw new UsageError(
|
|
106
161
|
`${slug} agent not found in organization "${SYSTEM_AGENT_ORG}"\n\n` +
|
|
@@ -117,7 +172,11 @@ async function resolveDraftAgent(
|
|
|
117
172
|
// explicit -o → that directory
|
|
118
173
|
// local workspace → skip (the agent writes directly to disk)
|
|
119
174
|
// otherwise → current directory
|
|
120
|
-
function resolveDownloadDir(
|
|
175
|
+
function resolveDownloadDir(
|
|
176
|
+
output: string,
|
|
177
|
+
detach: boolean,
|
|
178
|
+
localRoots: readonly string[],
|
|
179
|
+
): string {
|
|
121
180
|
if (detach) return "";
|
|
122
181
|
if (output !== "") return output;
|
|
123
182
|
if (localRoots.length > 0) return "";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// `stigmer internal-daemon` (hidden) — the long-lived supervised daemon process
|
|
2
2
|
// that `up` re-execs. It is not meant to be invoked directly; it runs the local
|
|
3
3
|
// stack until it receives SIGTERM/SIGINT, then exits with the daemon's code.
|
|
4
|
+
// (`stigmer up --foreground` runs the same body in the launching process.)
|
|
4
5
|
|
|
5
6
|
import type { Command } from "commander";
|
|
6
7
|
|
|
@@ -9,17 +10,8 @@ export function registerInternalDaemon(program: Command): void {
|
|
|
9
10
|
.command("internal-daemon", { hidden: true })
|
|
10
11
|
.description("internal: run the supervised local-stack daemon (do not invoke directly)")
|
|
11
12
|
.action(async () => {
|
|
12
|
-
const { runInternalDaemon } = await import("../local/daemon/process.js");
|
|
13
|
+
const { runInternalDaemon, waitForShutdownSignal } = await import("../local/daemon/process.js");
|
|
13
14
|
const code = await runInternalDaemon({ waitForShutdown: waitForShutdownSignal });
|
|
14
15
|
process.exit(code);
|
|
15
16
|
});
|
|
16
17
|
}
|
|
17
|
-
|
|
18
|
-
// Resolve when the first SIGTERM/SIGINT arrives — the daemon's shutdown trigger.
|
|
19
|
-
function waitForShutdownSignal(): Promise<void> {
|
|
20
|
-
return new Promise((resolve) => {
|
|
21
|
-
const onSignal = (): void => resolve();
|
|
22
|
-
process.once("SIGTERM", onSignal);
|
|
23
|
-
process.once("SIGINT", onSignal);
|
|
24
|
-
});
|
|
25
|
-
}
|
package/src/commands/run.ts
CHANGED
|
@@ -5,10 +5,14 @@
|
|
|
5
5
|
// the differ) load lazily inside the action so `--help` stays fast (DD-001).
|
|
6
6
|
|
|
7
7
|
import type { Command } from "commander";
|
|
8
|
-
import {
|
|
8
|
+
import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
|
|
9
|
+
import { ensureAuthenticated, resolveOrganization } from "../config/index.js";
|
|
9
10
|
import { UsageError } from "../errors/index.js";
|
|
10
11
|
import { interactiveBrowseEnabled } from "../resources/picker/tty.js";
|
|
11
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
addAgentExecFlags,
|
|
14
|
+
type AgentExecOptions,
|
|
15
|
+
} from "./agent-exec-flags.js";
|
|
12
16
|
import { globalOrg } from "./shared.js";
|
|
13
17
|
|
|
14
18
|
interface RunFlags extends AgentExecOptions {
|
|
@@ -23,8 +27,13 @@ export function registerRun(program: Command): void {
|
|
|
23
27
|
addAgentExecFlags(run)
|
|
24
28
|
.option("--json", "stream events as newline-delimited JSON")
|
|
25
29
|
.option("--download <dir>", "download artifacts to directory when complete")
|
|
26
|
-
.action(
|
|
27
|
-
|
|
30
|
+
.action(
|
|
31
|
+
(
|
|
32
|
+
type: string | undefined,
|
|
33
|
+
reference: string | undefined,
|
|
34
|
+
options: RunFlags,
|
|
35
|
+
command: Command,
|
|
36
|
+
) => runRun(type, reference, options, command),
|
|
28
37
|
);
|
|
29
38
|
}
|
|
30
39
|
|
|
@@ -76,7 +85,8 @@ async function runSmart(
|
|
|
76
85
|
outputMode: "inline" | "json",
|
|
77
86
|
client: import("../client/index.js").BackendClient,
|
|
78
87
|
): Promise<void> {
|
|
79
|
-
const { isSessionId, hasResourceIdPrefix, isAgentId, validateResourceId } =
|
|
88
|
+
const { isSessionId, hasResourceIdPrefix, isAgentId, validateResourceId } =
|
|
89
|
+
await import("../resources/reference.js");
|
|
80
90
|
|
|
81
91
|
if (isSessionId(value)) {
|
|
82
92
|
throw new UsageError(
|
|
@@ -126,13 +136,14 @@ async function runExplicit(
|
|
|
126
136
|
client: import("../client/index.js").BackendClient,
|
|
127
137
|
): Promise<void> {
|
|
128
138
|
const { defaultRegistry, Verb } = await import("../registry/index.js");
|
|
129
|
-
const { ApiResourceKind } =
|
|
130
|
-
"@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb"
|
|
131
|
-
);
|
|
139
|
+
const { ApiResourceKind } =
|
|
140
|
+
await import("@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb");
|
|
132
141
|
|
|
133
142
|
const info = defaultRegistry().getByAlias(type);
|
|
134
143
|
if (info === undefined) {
|
|
135
|
-
throw new UsageError(
|
|
144
|
+
throw new UsageError(
|
|
145
|
+
`unknown resource type: ${type}\n\nAvailable types: agent, workflow`,
|
|
146
|
+
);
|
|
136
147
|
}
|
|
137
148
|
if (!info.supportedVerbs.has(Verb.Run)) {
|
|
138
149
|
throw new UsageError(`${info.displayName} does not support 'run'`);
|
|
@@ -168,15 +179,26 @@ async function runResolvedAgent(
|
|
|
168
179
|
client: import("../client/index.js").BackendClient,
|
|
169
180
|
): Promise<void> {
|
|
170
181
|
const { prepareAgentExec } = await import("../resources/run/prepare.js");
|
|
171
|
-
const { executeResolvedAgent } =
|
|
182
|
+
const { executeResolvedAgent } =
|
|
183
|
+
await import("../resources/run/agent-exec.js");
|
|
172
184
|
const { toAgentExecFlags } = await import("./agent-exec-flags.js");
|
|
173
185
|
|
|
174
|
-
const prepared = await prepareAgentExec(
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
186
|
+
const prepared = await prepareAgentExec(
|
|
187
|
+
toAgentExecFlags(options),
|
|
188
|
+
client.stigmer,
|
|
189
|
+
org,
|
|
190
|
+
stderrProgress(),
|
|
191
|
+
{
|
|
192
|
+
// The kind's tier against the server's reported edition — the same
|
|
193
|
+
// question the console asks — never the config's backend type.
|
|
194
|
+
accountPreferencesAvailable: await client.isResourceAvailable(
|
|
195
|
+
ApiResourceKind.identity_account,
|
|
196
|
+
),
|
|
197
|
+
// run opts into the account default_harness fill; draft deliberately
|
|
198
|
+
// does not (see the option's doc comment for the D5 rationale).
|
|
199
|
+
applyAccountHarnessDefault: true,
|
|
200
|
+
},
|
|
201
|
+
);
|
|
180
202
|
await executeResolvedAgent({
|
|
181
203
|
agent,
|
|
182
204
|
prepared,
|
|
@@ -199,15 +221,21 @@ async function runWorkflow(
|
|
|
199
221
|
client: import("../client/index.js").BackendClient,
|
|
200
222
|
): Promise<void> {
|
|
201
223
|
if (options.workspace.length > 0) {
|
|
202
|
-
throw new UsageError(
|
|
224
|
+
throw new UsageError(
|
|
225
|
+
"--workspace is not supported for workflows (workspace is an agent-level concept)",
|
|
226
|
+
);
|
|
203
227
|
}
|
|
204
|
-
const [
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
228
|
+
const [
|
|
229
|
+
{ resolveWorkflowRef },
|
|
230
|
+
{ createWorkflowExecution },
|
|
231
|
+
{ loadAndMergeEnv },
|
|
232
|
+
{ parseApprovalAction },
|
|
233
|
+
] = await Promise.all([
|
|
234
|
+
import("../resources/run/resolve.js"),
|
|
235
|
+
import("../resources/run/create.js"),
|
|
236
|
+
import("../resources/run/env.js"),
|
|
237
|
+
import("../resources/run/prepare.js"),
|
|
238
|
+
]);
|
|
211
239
|
|
|
212
240
|
const workflow = await resolveWorkflowRef(client.stigmer, reference, org);
|
|
213
241
|
const runtimeEnv = loadAndMergeEnv({
|
|
@@ -220,27 +248,37 @@ async function runWorkflow(
|
|
|
220
248
|
runtimeEnv.STIGMER_ORG_ID = { value: org, isSecret: false };
|
|
221
249
|
}
|
|
222
250
|
|
|
223
|
-
const execution = await createWorkflowExecution(
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
251
|
+
const execution = await createWorkflowExecution(
|
|
252
|
+
client.controller.bind(client),
|
|
253
|
+
{
|
|
254
|
+
workflowId: workflow.metadata?.id ?? "",
|
|
255
|
+
orgId: org,
|
|
256
|
+
message: options.message ?? "",
|
|
257
|
+
runtimeEnv,
|
|
258
|
+
},
|
|
259
|
+
);
|
|
229
260
|
const id = execution.metadata?.id ?? "";
|
|
230
261
|
|
|
231
262
|
if (options.detach === true) {
|
|
232
263
|
process.stdout.write(`Workflow execution created: ${id}\n`);
|
|
233
|
-
process.stdout.write(
|
|
264
|
+
process.stdout.write(
|
|
265
|
+
`Track it with: stigmer execution logs ${id} --follow\n`,
|
|
266
|
+
);
|
|
234
267
|
return;
|
|
235
268
|
}
|
|
236
269
|
|
|
237
|
-
const { ApprovalAction } =
|
|
238
|
-
|
|
270
|
+
const { ApprovalAction } =
|
|
271
|
+
await import("@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb");
|
|
272
|
+
const { streamWorkflowExecution } =
|
|
273
|
+
await import("../resources/run/workflow-stream.js");
|
|
239
274
|
await streamWorkflowExecution({
|
|
240
275
|
client: client.stigmer,
|
|
241
276
|
executionId: id,
|
|
242
277
|
outputMode,
|
|
243
|
-
defaultAction:
|
|
278
|
+
defaultAction:
|
|
279
|
+
options.autoApprove === true
|
|
280
|
+
? ApprovalAction.APPROVE_ALL
|
|
281
|
+
: parseApprovalAction(options.approveDefault ?? ""),
|
|
244
282
|
});
|
|
245
283
|
}
|
|
246
284
|
|
|
@@ -260,7 +298,11 @@ async function browseAndRunAgent(
|
|
|
260
298
|
client: import("../client/index.js").BackendClient,
|
|
261
299
|
): Promise<void> {
|
|
262
300
|
const { pickAgent } = await import("../resources/picker/ink.js");
|
|
263
|
-
const selected = await pickAgent({
|
|
301
|
+
const selected = await pickAgent({
|
|
302
|
+
client: client.stigmer,
|
|
303
|
+
org,
|
|
304
|
+
initialQuery,
|
|
305
|
+
});
|
|
264
306
|
if (selected === undefined) return;
|
|
265
307
|
await runAgent(selected.id, options, org, outputMode, client);
|
|
266
308
|
}
|
package/src/commands/up.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// `stigmer up [server]` — start the local Stigmer stack (managed Temporal, the
|
|
2
2
|
// control-plane server, and the unified runner) as a supervised background
|
|
3
|
-
// daemon. `up server` brings up
|
|
3
|
+
// daemon, or with `--foreground` as this very process. `up server` brings up
|
|
4
|
+
// only the control plane.
|
|
4
5
|
//
|
|
5
6
|
// Thin handler: parse flags, delegate to the local daemon launcher, render the
|
|
6
7
|
// outcome. The launcher (and the heavy resolvers it pulls in) load lazily so
|
|
@@ -9,6 +10,8 @@
|
|
|
9
10
|
import { homedir } from "node:os";
|
|
10
11
|
import { join } from "node:path";
|
|
11
12
|
import type { Command } from "commander";
|
|
13
|
+
import { CliExitError } from "../errors/cli-exit-error.js";
|
|
14
|
+
import { ExitCode } from "../errors/exit-codes.js";
|
|
12
15
|
import { CommandResult, type OutputFlags, renderResult } from "../output/index.js";
|
|
13
16
|
import { HEALTH_STATE_FILE, SERVER_PORT } from "../local/constants.js";
|
|
14
17
|
import { addResultFlags, resultFormat } from "./shared.js";
|
|
@@ -16,6 +19,13 @@ import { addResultFlags, resultFormat } from "./shared.js";
|
|
|
16
19
|
interface UpFlags extends OutputFlags {
|
|
17
20
|
serverOnly?: boolean;
|
|
18
21
|
web?: boolean; // false when --no-web is passed
|
|
22
|
+
foreground?: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface UpRun {
|
|
26
|
+
serverOnly: boolean;
|
|
27
|
+
noWeb: boolean;
|
|
28
|
+
foreground: boolean;
|
|
19
29
|
}
|
|
20
30
|
|
|
21
31
|
// The server serves the web console from its unified port (DD-012); the
|
|
@@ -23,14 +33,23 @@ interface UpFlags extends OutputFlags {
|
|
|
23
33
|
// process, one origin — there is no separate console to not-start).
|
|
24
34
|
const NO_WEB_HELP = "don't report the web console URL";
|
|
25
35
|
|
|
36
|
+
// Foreground is the shape a supervisor wants — a container entrypoint, a
|
|
37
|
+
// systemd unit, a tmux pane: one process to watch, signals delivered to it,
|
|
38
|
+
// the stack's output on its stdio — instead of a detached daemon to poll.
|
|
39
|
+
const FOREGROUND_HELP = "run the stack in this process until Ctrl-C or SIGTERM (for containers and service managers)";
|
|
40
|
+
|
|
26
41
|
export function registerUp(program: Command): void {
|
|
27
42
|
const up = program
|
|
28
43
|
.command("up")
|
|
29
44
|
.description("start the local Stigmer stack (server, runner, Temporal)")
|
|
30
45
|
.option("--server-only", "start only the control plane (no runners)")
|
|
31
46
|
.option("--no-web", NO_WEB_HELP)
|
|
47
|
+
.option("--foreground", FOREGROUND_HELP)
|
|
32
48
|
.action((options: UpFlags) =>
|
|
33
|
-
runUp(
|
|
49
|
+
runUp(
|
|
50
|
+
{ serverOnly: options.serverOnly === true, noWeb: options.web === false, foreground: options.foreground === true },
|
|
51
|
+
options,
|
|
52
|
+
),
|
|
34
53
|
);
|
|
35
54
|
addResultFlags(up);
|
|
36
55
|
|
|
@@ -38,16 +57,40 @@ export function registerUp(program: Command): void {
|
|
|
38
57
|
.command("server")
|
|
39
58
|
.description("start only the control plane (no runners)")
|
|
40
59
|
.option("--no-web", NO_WEB_HELP)
|
|
41
|
-
.
|
|
60
|
+
.option("--foreground", FOREGROUND_HELP)
|
|
61
|
+
.action((options: UpFlags) =>
|
|
62
|
+
runUp({ serverOnly: true, noWeb: options.web === false, foreground: options.foreground === true }, options),
|
|
63
|
+
);
|
|
42
64
|
addResultFlags(server);
|
|
43
65
|
}
|
|
44
66
|
|
|
45
|
-
async function runUp(
|
|
67
|
+
async function runUp(run: UpRun, flags: OutputFlags): Promise<void> {
|
|
46
68
|
process.stderr.write("Starting Stigmer local stack… (first run may take a moment)\n");
|
|
47
|
-
const
|
|
48
|
-
|
|
69
|
+
const launch = await import("../local/daemon/launch.js");
|
|
70
|
+
|
|
71
|
+
if (!run.foreground) {
|
|
72
|
+
await launch.up(run);
|
|
73
|
+
await renderUpCard(run, flags);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// The card renders when the stack is serving and seeded — the moment the
|
|
78
|
+
// detached shape would have returned — and the process then stays up,
|
|
79
|
+
// mirroring the server's and runner's output, until a shutdown signal.
|
|
80
|
+
const code = await launch.upForeground(run, homedir(), {
|
|
81
|
+
onReady: () => renderUpCard(run, flags),
|
|
82
|
+
});
|
|
83
|
+
if (code !== 0) {
|
|
84
|
+
// The daemon body has already logged which component failed and why.
|
|
85
|
+
const { logDir } = await import("../local/paths.js");
|
|
86
|
+
throw new CliExitError("the local stack did not start", ExitCode.General, [
|
|
87
|
+
`Check the component logs under ${logDir(homedir())}.`,
|
|
88
|
+
]);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
49
91
|
|
|
50
|
-
|
|
92
|
+
async function renderUpCard(run: UpRun, flags: OutputFlags): Promise<void> {
|
|
93
|
+
const result = CommandResult.success(run.serverOnly ? "Stigmer control plane is up" : "Stigmer local stack is up");
|
|
51
94
|
const section = result.addSection("Endpoints");
|
|
52
95
|
section.field("server", `http://localhost:${SERVER_PORT}`);
|
|
53
96
|
if (await consoleReported()) {
|
|
@@ -56,7 +99,8 @@ async function runUp(opts: { serverOnly: boolean; noWeb: boolean }, flags: Outpu
|
|
|
56
99
|
// server without one must not advertise a dead URL.
|
|
57
100
|
section.field("console", `http://localhost:${SERVER_PORT}`);
|
|
58
101
|
}
|
|
59
|
-
result.hint("Check status with: stigmer status")
|
|
102
|
+
result.hint("Check status with: stigmer status");
|
|
103
|
+
result.hint(run.foreground ? "Stop it with: Ctrl-C (or stigmer down from another shell)" : "Stop it with: stigmer down");
|
|
60
104
|
renderResult(result, resultFormat(flags));
|
|
61
105
|
}
|
|
62
106
|
|