@domesystems/templates 0.4.1 → 0.4.3
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/package.json +1 -1
- package/src/cli.mjs +10 -8
- package/src/generate.mjs +20 -25
- package/src/manifest.mjs +18 -10
package/package.json
CHANGED
package/src/cli.mjs
CHANGED
|
@@ -61,10 +61,11 @@ function generate(options, output) {
|
|
|
61
61
|
output(line("success", `Generated ${file}`, options));
|
|
62
62
|
}
|
|
63
63
|
function runtimeFor(directory, manifest, selectedAgent) {
|
|
64
|
-
const
|
|
65
|
-
|
|
64
|
+
const external = manifest.agents.filter((agent) => agent.type === "external");
|
|
65
|
+
const candidates = selectedAgent ? manifest.agents.filter((agent) => agent.name === selectedAgent) : (external.length ? external : manifest.agents);
|
|
66
|
+
if (candidates.length !== 1) throw new Error(`templates up requires exactly one agent to run; found ${candidates.length}. Pass --agent <name> to select one.`);
|
|
66
67
|
const agent = candidates[0];
|
|
67
|
-
if (agent.type
|
|
68
|
+
if (agent.type === "managed") return { ...agent, managed: true };
|
|
68
69
|
const configPath = path.join("agents", agent.name);
|
|
69
70
|
const configFile = path.join(directory, configPath, "agent.yaml");
|
|
70
71
|
if (!fs.existsSync(configFile)) throw new Error(`External agent ${agent.name} needs ${configPath}/agent.yaml.`);
|
|
@@ -115,10 +116,10 @@ async function up(options, output) {
|
|
|
115
116
|
const target = path.resolve(process.cwd(), options.directory ?? ".");
|
|
116
117
|
const manifest = readManifest(target);
|
|
117
118
|
const runtimeAgent = runtimeFor(target, manifest, options.agent);
|
|
118
|
-
const needsCredentials = !configured(target);
|
|
119
|
+
const needsCredentials = !runtimeAgent.managed && !configured(target);
|
|
119
120
|
output(line("info", `Checking ${target}`, options));
|
|
120
|
-
fs.writeFileSync(path.join(target, ".dome-compose.yaml"), renderCompose(runtimeAgent.runtime, options.port, { debug: options.debug, configPath: runtimeAgent.configPath }));
|
|
121
|
-
if (options.importJob) await configureRuntime(target, options.importJob, runtimeAgent.name, runtimeAgent.localKey, options, output);
|
|
121
|
+
if (!runtimeAgent.managed) fs.writeFileSync(path.join(target, ".dome-compose.yaml"), renderCompose(runtimeAgent.runtime, options.port, { debug: options.debug, configPath: runtimeAgent.configPath }));
|
|
122
|
+
if (options.importJob && !runtimeAgent.managed) await configureRuntime(target, options.importJob, runtimeAgent.name, runtimeAgent.localKey, options, output);
|
|
122
123
|
else if (options.provision && (needsCredentials || options.forceProvision)) {
|
|
123
124
|
output(line("info", "Setting up Dome resources", options));
|
|
124
125
|
let imported;
|
|
@@ -126,10 +127,11 @@ async function up(options, output) {
|
|
|
126
127
|
catch (caught) { const dashboardURL = importDashboardURL(target); throw new Error(`${caught.message}${dashboardURL ? ` View the import details: ${dashboardURL}` : ""}`); }
|
|
127
128
|
const job = imported?.data?.job;
|
|
128
129
|
if (!job?.id) throw new Error("Dome import did not return a job ID.");
|
|
129
|
-
if (importCreatedResources(job) && importHasPendingOutputs(job)) await configureRuntime(target, job.id, runtimeAgent.name, runtimeAgent.localKey, options, output);
|
|
130
|
+
if (!runtimeAgent.managed && importCreatedResources(job) && importHasPendingOutputs(job)) await configureRuntime(target, job.id, runtimeAgent.name, runtimeAgent.localKey, options, output);
|
|
130
131
|
else if (needsCredentials) throw new Error(`Dome matched existing resources and did not create a new runtime token. Recover a pending import with templates up --dir ${options.directory ?? "."} --import-job <job-id>, or rotate the local agent key and set DOME_TOKEN and DOME_GATEWAY_URL in .env.`);
|
|
131
132
|
}
|
|
132
|
-
if (options.start) { const compose = composeCommands(options); run("docker", compose.pull, target); run("docker", compose.up, target); }
|
|
133
|
+
if (options.start && !runtimeAgent.managed) { const compose = composeCommands(options); run("docker", compose.pull, target); run("docker", compose.up, target); }
|
|
134
|
+
if (runtimeAgent.managed) output(line("success", `Managed agent ${runtimeAgent.name} is provisioned in Dome; Dome owns its loop and triggers.`, options));
|
|
133
135
|
}
|
|
134
136
|
async function publish(options, output, fetchImplementation) { const target = path.resolve(process.cwd(), options.directory ?? "."); const manifest = readPublishManifest(target); const source = publishSource(target); const name = manifest.metadata.name; const [scope, slug] = name.split("/"); const archive = options.dryRun ? null : createArchive(source); output(line("info", `Checking ${name}@${manifest.metadata.version}`, options)); const payload = { name, version: manifest.metadata.version, title: manifest.metadata.title, summary: manifest.metadata.summary ?? "", sha256: archive?.sha256, size_bytes: archive?.buffer.length, source: { repository: manifest.metadata.source.repository, path: manifest.metadata.source.path, commit: source.commit, remote: source.remote, files: source.files } };
|
|
135
137
|
const remote = remoteTarget(options); let registry; let attempt;
|
package/src/generate.mjs
CHANGED
|
@@ -70,21 +70,22 @@ function managedModelReference(manifest, name) {
|
|
|
70
70
|
throw new Error(`Managed agent default_model ${name} must name a pool or model.`);
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
function
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
});
|
|
73
|
+
function agentAttributes(agent, managed) {
|
|
74
|
+
const lines = [];
|
|
75
|
+
if (agent.parent) lines.push(attr("parent", `dome_agent.${label(agent.parent)}.name`));
|
|
76
|
+
if (agent.metadata) lines.push(attr("metadata", `{ ${Object.entries(agent.metadata).map(([key, value]) => `${q(key)} = ${q(value)}`).join(", ")} }`));
|
|
77
|
+
for (const [key, resource] of [["allowed_pools", "dome_llm_pool"], ["allowed_models", "dome_llm_connection"], ...(!managed ? [["allowed_gateways", "dome_gateway"]] : [])]) {
|
|
78
|
+
if (agent[key] !== undefined) lines.push(attr(key, `[${agent[key].map((name) => `${resource}.${label(name)}.name`).join(", ")}]`));
|
|
79
|
+
}
|
|
80
|
+
if (!managed && agent.allowed_tools !== undefined) lines.push(attr("allowed_tools", `[${agent.allowed_tools.map(q).join(", ")}]`));
|
|
81
|
+
for (const key of ["suspended", "token_ttl", "interactive_access", "secrets_version"]) if (agent[key] !== undefined) lines.push(attr(key, typeof agent[key] === "string" ? q(agent[key]) : String(agent[key])));
|
|
82
|
+
if (agent.act_as) {
|
|
83
|
+
const values = Object.entries(agent.act_as).filter(([key]) => !["hmac_secret"].includes(key)).map(([key, value]) => `${key} = ${typeof value === "string" ? q(value) : String(value)}`);
|
|
84
|
+
lines.push(attr("act_as", `{ ${values.join(", ")} }`));
|
|
85
|
+
if (agent.act_as.hmac_secret) lines.push(attr("act_as_hmac_secret", expression(agent.act_as.hmac_secret)));
|
|
86
|
+
}
|
|
87
|
+
for (const key of ["actas_allowed_groups", "actas_allowed_emails", "actas_allowed_subjects"]) if (agent[key] !== undefined) lines.push(attr(key, `[${agent[key].map(q).join(", ")}]`));
|
|
88
|
+
return lines;
|
|
88
89
|
}
|
|
89
90
|
|
|
90
91
|
export function renderDomeTerraform(manifest) {
|
|
@@ -117,23 +118,17 @@ export function renderDomeTerraform(manifest) {
|
|
|
117
118
|
}
|
|
118
119
|
for (const agent of manifest.agents) {
|
|
119
120
|
const agentLabel = label(agent.name); const managed = agent.type === "managed"; const resourceType = managed ? "dome_managed_agent" : "dome_agent";
|
|
120
|
-
const lines = [attr("workspace_id", "var.workspace_id"), attr("name", q(agent.name))];
|
|
121
|
-
if (!managed) lines.push(attr("allowed_gateways", `[${(agent.allowed_gateways ?? []).map((gateway) => `dome_gateway.${label(gateway)}.name`).join(", ")}]`));
|
|
122
|
-
if (!managed && agent.act_as) {
|
|
123
|
-
const { method, required: actAsRequired, hmac_secret: hmacSecret, allowed_subjects: allowedSubjects } = agent.act_as;
|
|
124
|
-
if (!method) throw new Error(`Agent ${agent.name} act_as needs method.`);
|
|
125
|
-
lines.push(attr("act_as", `{ method = ${q(method)}, required = ${Boolean(actAsRequired)} }`));
|
|
126
|
-
if (hmacSecret) lines.push(attr("act_as_hmac_secret", expression(hmacSecret)));
|
|
127
|
-
if (allowedSubjects) lines.push(attr("actas_allowed_subjects", `[${allowedSubjects.map(q).join(", ")}]`));
|
|
128
|
-
}
|
|
121
|
+
const lines = [attr("workspace_id", "var.workspace_id"), attr("name", q(agent.name)), ...agentAttributes(agent, managed)];
|
|
129
122
|
if (managed) {
|
|
130
123
|
if (!agent.system_prompt?.content || !agent.default_model) throw new Error(`Managed agent ${agent.name} needs system_prompt and default_model.`);
|
|
124
|
+
if (typeof agent.gateway !== "string" || !agent.gateway) throw new Error(`Managed agent ${agent.name} needs gateway.`);
|
|
125
|
+
if (!manifest.gateways.some((gateway) => gateway.name === agent.gateway)) throw new Error(`Managed agent ${agent.name} references unknown gateway ${agent.gateway}.`);
|
|
131
126
|
lines.push(attr("system_prompt", "<<-PROMPT"), ...agent.system_prompt.content.split("\n").map((line) => line.trimEnd()), "PROMPT", attr("default_model", managedModelReference(manifest, agent.default_model)), attr("gateway", `dome_gateway.${label(agent.gateway)}.name`));
|
|
132
127
|
}
|
|
133
128
|
blocks.push(block(resourceType, agentLabel, lines), ...rulesBlock(agent, resourceType, agentLabel));
|
|
134
129
|
if (!managed) for (const key of agent.keys ?? []) blocks.push(block("dome_agent_key", `${agentLabel}_${label(required(key, "name", `Agent ${agent.name} keys need a name.`))}`, [attr("workspace_id", "var.workspace_id"), attr("agent", `dome_agent.${agentLabel}.name`), attr("name", q(key.name))]));
|
|
135
130
|
}
|
|
136
|
-
blocks.push(...filterBlocks(manifest.filters), ...quotaBlocks(manifest.quotas, new Map(manifest.agents.map((agent) => [agent.name, agent.type])))
|
|
131
|
+
blocks.push(...filterBlocks(manifest.filters), ...quotaBlocks(manifest.quotas, new Map(manifest.agents.map((agent) => [agent.name, agent.type]))));
|
|
137
132
|
const header = `# Generated by @domesystems/templates from template.yaml. Do not edit.\nterraform {\n required_providers {\n dome = { source = ${q("dome-systems/dome")}, version = ${q(">= 0.1.0")} }\n }\n}\n\nvariable "workspace_id" { type = string }`;
|
|
138
133
|
const outputs = manifest.agents.filter((agent) => agent.type === "external").flatMap((agent) => (agent.keys ?? []).map((key) => {
|
|
139
134
|
const agentLabel = label(agent.name); const keyLabel = label(key.name);
|
package/src/manifest.mjs
CHANGED
|
@@ -19,10 +19,13 @@ function sourcePath(root, reference, label) {
|
|
|
19
19
|
function expandAgent(root, entry, index) {
|
|
20
20
|
if (!entry || typeof entry !== "object") throw new Error(`agents[${index}] must be an object or path.`);
|
|
21
21
|
if (entry.path === undefined) return entry;
|
|
22
|
-
if (Object.keys(entry).
|
|
23
|
-
|
|
22
|
+
if (Object.keys(entry).some((key) => key !== "name" && key !== "path")) throw new Error(`agents[${index}] may contain only name and path when path-backed.`);
|
|
23
|
+
if (typeof entry.name !== "string" || !entry.name.trim()) throw new Error(`agents[${index}].name is required when path-backed.`);
|
|
24
|
+
const file = sourcePath(root, entry.path, `agents[${index}]`);
|
|
25
|
+
const agent = parseYAML(file, `agent fragment ${entry.path}`);
|
|
24
26
|
if (!agent || typeof agent !== "object" || Array.isArray(agent)) throw new Error(`Agent fragment ${entry.path} must be an object.`);
|
|
25
|
-
|
|
27
|
+
if (agent.name !== undefined) throw new Error(`Agent fragment ${entry.path} must not declare name; template.yaml owns the agent identity.`);
|
|
28
|
+
return { ...agent, name: entry.name, __source_directory: path.dirname(file) };
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
function expandFilters(root, filters) {
|
|
@@ -74,8 +77,6 @@ function normalizeManagedAgent(root, agent, index) {
|
|
|
74
77
|
const hasPath = typeof prompt.path === "string";
|
|
75
78
|
const hasInline = typeof prompt.inline === "string";
|
|
76
79
|
if (hasPath === hasInline) throw new Error(`agents[${index}].system_prompt must declare exactly one of path or inline.`);
|
|
77
|
-
if (typeof agent.gateway !== "string" || !agent.gateway.trim()) throw new Error(`Managed agent ${agent.name} needs gateway.`);
|
|
78
|
-
if (agent.allowed_gateways !== undefined || agent.trigger !== undefined) throw new Error(`Managed agent ${agent.name} uses gateway and top-level triggers; allowed_gateways and trigger are not supported.`);
|
|
79
80
|
return { ...agent, system_prompt: { content: hasPath ? fs.readFileSync(sourcePath(root, prompt.path, `agents[${index}].system_prompt`), "utf8") : prompt.inline, source: hasPath ? prompt.path : "inline" } };
|
|
80
81
|
}
|
|
81
82
|
|
|
@@ -96,18 +97,25 @@ export function readManifest(templateDirectory) {
|
|
|
96
97
|
if (manifest.apiVersion !== "templates.domesystems.ai/v1" || manifest.kind !== "Template") throw new Error("template.yaml must declare apiVersion templates.domesystems.ai/v1 and kind Template.");
|
|
97
98
|
if (!Array.isArray(manifest.agents) || manifest.agents.length === 0) throw new Error("template.yaml must declare at least one agent.");
|
|
98
99
|
const agents = manifest.agents.map((entry, index) => {
|
|
99
|
-
const
|
|
100
|
+
const expanded = expandAgent(root, entry, index);
|
|
101
|
+
const agentRoot = expanded.__source_directory ?? root;
|
|
102
|
+
const agent = normalizeManagedAgent(agentRoot, normalizeRuleFiles(agentRoot, expanded, index), index);
|
|
100
103
|
if (typeof agent.name !== "string" || !agent.name.trim()) throw new Error(`agents[${index}].name is required.`);
|
|
101
104
|
const type = agent.type;
|
|
102
105
|
if (!type) throw new Error(`agents[${index}].type is required.`);
|
|
103
106
|
if (!["external", "managed"].includes(type)) throw new Error(`agents[${index}].type must be external or managed.`);
|
|
104
107
|
if (type === "managed" && agent.keys?.length) throw new Error(`agents[${index}] is managed and cannot declare keys.`);
|
|
105
|
-
|
|
108
|
+
const { __source_directory, ...cleanAgent } = agent;
|
|
109
|
+
return { ...cleanAgent, type };
|
|
106
110
|
});
|
|
107
111
|
const filters = normalizeFilterConfigs(root, expandFilters(root, manifest.filters));
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
112
|
+
for (const agent of agents) {
|
|
113
|
+
if (agent.type === "managed" && (!agent.system_prompt || !agent.default_model || !agent.gateway)) throw new Error(`Managed agent ${agent.name} needs system_prompt, default_model, and gateway.`);
|
|
114
|
+
if (agent.type === "managed" && (agent.allowed_gateways !== undefined || agent.allowed_tools !== undefined || agent.trigger_config !== undefined)) throw new Error(`Managed agent ${agent.name} uses gateway; allowed_gateways, allowed_tools, and trigger_config are not supported.`);
|
|
115
|
+
if (agent.type === "external" && (agent.system_prompt !== undefined || agent.default_model !== undefined || agent.gateway !== undefined || agent.trigger_config !== undefined)) throw new Error(`External agent ${agent.name} cannot declare managed-agent loop fields.`);
|
|
116
|
+
}
|
|
117
|
+
named(agents, "agents"); named(manifest.gateways ?? [], "gateways"); named(manifest.tool_connections ?? [], "tool_connections"); named(manifest.models ?? [], "models"); named(manifest.pools ?? [], "pools"); named(filters, "filters");
|
|
118
|
+
return { ...manifest, variables: manifest.variables ?? {}, gateways: manifest.gateways ?? [], tool_connections: manifest.tool_connections ?? [], models: manifest.models ?? [], pools: manifest.pools ?? [], filters, quotas: manifest.quotas ?? [], agents };
|
|
111
119
|
}
|
|
112
120
|
|
|
113
121
|
const PACKAGE_NAME = /^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?\/[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/;
|