microsoft/TypeAgent

Public

mirrored from https://github.com/microsoft/TypeAgentAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
a0141f2e0c960f2b6b63751392c0fa741f3a7bf8

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

ts/tools/scripts/deployAgentServer.mjs

195lines · modecode

1#!/usr/bin/env node
2// Copyright (c) Microsoft Corporation.
3// Licensed under the MIT License.
4
5/**
6 * Build a self-contained, repo-less-runnable agent-server artifact.
7 *
8 * Approach (see codeDocs .../2026-06-11_typeagent-plugin-agent-distribution):
9 * the agent-server loads on-disk data assets (agent manifests, grammars) via
10 * getPackageFilePath, so it CANNOT be a single-file esbuild bundle. Instead we
11 * use `pnpm deploy` to produce a folder with a flat node_modules (workspace
12 * packages copied in, so data-asset resolution keeps working), prune
13 * foreign-arch native binaries, and copy in the config-provisioning tool
14 * (getKeys) + the bootstrap launcher (typeagent-serve).
15 *
16 * Result layout (<out>):
17 * dist/server.js the daemon entry
18 * node_modules/ deployed + pruned
19 * typeagent-serve.mjs bootstrap launcher (start / provision / status)
20 * tools/getKeys.mjs (+config, lib/, config.sample.yaml)
21 *
22 * Usage (from ts/):
23 * node tools/scripts/deployAgentServer.mjs --out <dir> [--platform win32] [--arch x64]
24 * [--skip-deploy] [--skip-prune]
25 */
26
27import { spawnSync } from "node:child_process";
28import fs from "node:fs";
29import path from "node:path";
30import { fileURLToPath } from "node:url";
31
32const scriptsDir = path.dirname(fileURLToPath(import.meta.url));
33const tsRoot = path.resolve(scriptsDir, "..", ".."); // ts/
34
35function parseArgs(argv) {
36 const args = { skipDeploy: false, skipPrune: false };
37 for (let i = 2; i < argv.length; i++) {
38 const a = argv[i];
39 if (a === "--out") args.out = argv[++i];
40 else if (a === "--platform") args.platform = argv[++i];
41 else if (a === "--arch") args.arch = argv[++i];
42 else if (a === "--skip-deploy") args.skipDeploy = true;
43 else if (a === "--skip-prune") args.skipPrune = true;
44 else if (a === "--profile") args.profile = argv[++i];
45 else if (a === "--external-cli") args.externalCli = true;
46 else throw new Error(`Unknown argument: ${a}`);
47 }
48 if (!args.out) throw new Error("Missing --out <dir>.");
49 args.out = path.resolve(args.out);
50 args.platform = args.platform ?? process.platform;
51 args.arch = args.arch ?? process.arch;
52 return args;
53}
54
55function run(cmd, cmdArgs, cwd) {
56 console.log(`> ${cmd} ${cmdArgs.join(" ")}`);
57 const res = spawnSync(cmd, cmdArgs, {
58 cwd,
59 stdio: "inherit",
60 shell: process.platform === "win32", // resolve pnpm.cmd on Windows
61 });
62 if (res.status !== 0) {
63 throw new Error(`Command failed (${res.status}): ${cmd}`);
64 }
65}
66
67function copyInto(srcAbs, destAbs) {
68 fs.mkdirSync(path.dirname(destAbs), { recursive: true });
69 fs.cpSync(srcAbs, destAbs, { recursive: true });
70 console.log(` copied ${path.relative(tsRoot, srcAbs)} -> ${destAbs}`);
71}
72
73function main() {
74 const args = parseArgs(process.argv);
75 console.log(
76 `Building agent-server artifact at ${args.out} (target ${args.platform}-${args.arch})`,
77 );
78
79 // 1. pnpm deploy (hoisted node-linker avoids the cross-drive symlink issue
80 // and yields a copied, self-contained node_modules).
81 if (!args.skipDeploy) {
82 fs.rmSync(args.out, { recursive: true, force: true });
83 run(
84 "pnpm",
85 [
86 "--filter",
87 "agent-server",
88 "--config.node-linker=hoisted",
89 "deploy",
90 "--prod",
91 args.out,
92 ],
93 tsRoot,
94 );
95 } else {
96 console.log("Skipping pnpm deploy (--skip-deploy).");
97 }
98
99 // 2. Prune foreign-arch native packages.
100 if (!args.skipPrune) {
101 run(
102 "node",
103 [
104 path.join(scriptsDir, "pruneDeploy.mjs"),
105 "--dir",
106 args.out,
107 "--platform",
108 args.platform,
109 "--arch",
110 args.arch,
111 ],
112 tsRoot,
113 );
114 } else {
115 console.log("Skipping prune (--skip-prune).");
116 }
117
118 // 2b. Apply an agent profile: drop agents (and deps reachable only through
119 // them) not in the selected profile, and record the profile so the
120 // launcher starts the daemon with it by default (the pruned artifact can
121 // no longer load the excluded agents).
122 if (args.profile) {
123 run(
124 "node",
125 [
126 path.join(scriptsDir, "pruneUnusedAgents.mjs"),
127 "--dir",
128 args.out,
129 "--profile",
130 args.profile,
131 ],
132 tsRoot,
133 );
134 fs.writeFileSync(
135 path.join(args.out, ".typeagent-profile"),
136 args.profile,
137 "utf8",
138 );
139 console.log(
140 ` recorded profile '${args.profile}' (.typeagent-profile)`,
141 );
142 }
143
144 // 2c. External-CLI variant: drop the bundled Claude/Copilot runtimes. Only
145 // valid where `claude`/`copilot` are on PATH (managed machines / the
146 // standalone installer); the runtime query() callers are wired to resolve
147 // the PATH binary (claudeExecutableOption), so they don't need the bundle.
148 if (args.externalCli) {
149 run(
150 "node",
151 [path.join(scriptsDir, "pruneSdkBinaries.mjs"), "--dir", args.out],
152 tsRoot,
153 );
154 fs.writeFileSync(
155 path.join(args.out, ".typeagent-external-cli"),
156 "claude,copilot must be on PATH\n",
157 "utf8",
158 );
159 console.log(" recorded external-cli mode (.typeagent-external-cli)");
160 }
161
162 // 3. Copy the config-provisioning tool (getKeys + its config/lib) and the
163 // config scaffold. getKeys' runtime deps are already in the deploy
164 // closure (chalk, @azure/keyvault-secrets, @azure/identity, js-yaml,
165 // @typeagent/config), so it runs as `node tools/getKeys.mjs`.
166 const toolsOut = path.join(args.out, "tools");
167 copyInto(
168 path.join(scriptsDir, "getKeys.mjs"),
169 path.join(toolsOut, "getKeys.mjs"),
170 );
171 copyInto(
172 path.join(scriptsDir, "getKeys.config.json"),
173 path.join(toolsOut, "getKeys.config.json"),
174 );
175 copyInto(path.join(scriptsDir, "lib"), path.join(toolsOut, "lib"));
176 const sample = path.join(tsRoot, "config.sample.yaml");
177 if (fs.existsSync(sample)) {
178 copyInto(sample, path.join(toolsOut, "config.sample.yaml"));
179 }
180
181 // 4. Copy the bootstrap launcher to the artifact root.
182 copyInto(
183 path.join(scriptsDir, "typeagent-serve.mjs"),
184 path.join(args.out, "typeagent-serve.mjs"),
185 );
186
187 console.log(
188 `\nArtifact ready at ${args.out}\n` +
189 ` Provision config: node typeagent-serve.mjs provision\n` +
190 ` Start the service: node typeagent-serve.mjs start\n` +
191 ` Check status: node typeagent-serve.mjs status`,
192 );
193}
194
195main();