hostctl 0.1.45 → 0.1.47

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/dist/index.d.ts CHANGED
@@ -7154,7 +7154,7 @@ declare class HostctlApi {
7154
7154
  }
7155
7155
 
7156
7156
  type TaskRegistry = {
7157
- register: (name: string, task: TaskFn) => TaskRegistry;
7157
+ register: <TParams extends TaskParams = TaskParams, TReturn extends RunFnReturnValue = RunFnReturnValue>(name: string, task: TaskFn<TParams, TReturn>) => TaskRegistry;
7158
7158
  get: (name: string) => TaskFn | undefined;
7159
7159
  tasks: () => Array<[string, TaskFn]>;
7160
7160
  has: (name: string) => boolean;
package/dist/index.js CHANGED
@@ -2718,7 +2718,7 @@ var Verbosity = {
2718
2718
  };
2719
2719
 
2720
2720
  // src/version.ts
2721
- var version = "0.1.45";
2721
+ var version = "0.1.47";
2722
2722
 
2723
2723
  // src/commands/pkg/create.ts
2724
2724
  import { promises as fs5 } from "fs";
@@ -2755,8 +2755,7 @@ var packageJsonTsTemplate = (packageName) => `{
2755
2755
  "author": "",
2756
2756
  "license": "MIT",
2757
2757
  "dependencies": {
2758
- "hostctl": "^0.1.42",
2759
- "zod": "^4.1.13"
2758
+ "hostctl": "^0.1.42"
2760
2759
  },
2761
2760
  "devDependencies": {
2762
2761
  "typescript": "^5.8.3",
@@ -2789,8 +2788,7 @@ var packageJsonJsTemplate = (packageName) => `{
2789
2788
  "author": "",
2790
2789
  "license": "MIT",
2791
2790
  "dependencies": {
2792
- "hostctl": "^0.1.42",
2793
- "zod": "^4.1.13"
2791
+ "hostctl": "^0.1.42"
2794
2792
  },
2795
2793
  "engines": {
2796
2794
  "node": ">=24"
@@ -2825,157 +2823,21 @@ function registryPrefixFromPackageName(packageName) {
2825
2823
  const normalized = withoutPrefix.replace(/[^a-zA-Z0-9]+/g, ".").replace(/^\.|\.$/g, "");
2826
2824
  return normalized || "example";
2827
2825
  }
2828
- var indexTsTemplate = (registryPrefix) => `import { createRegistry } from './registry.js';
2826
+ var indexTsTemplate = (registryPrefix) => `import { createRegistry } from 'hostctl';
2829
2827
  import hello from './tasks/hello.js';
2830
2828
 
2831
2829
  export { hello };
2832
2830
 
2833
2831
  export const registry = createRegistry().register('${registryPrefix}.hello', hello);
2834
2832
  `;
2835
- var indexJsTemplate = (registryPrefix) => `import { createRegistry } from './registry.js';
2833
+ var indexJsTemplate = (registryPrefix) => `import { createRegistry } from 'hostctl';
2836
2834
  import hello from './tasks/hello.js';
2837
2835
 
2838
2836
  export { hello };
2839
2837
 
2840
2838
  export const registry = createRegistry().register('${registryPrefix}.hello', hello);
2841
2839
  `;
2842
- var registryTsTemplate = `import type { TaskFn } from 'hostctl';
2843
-
2844
- export type TaskRegistry = {
2845
- register: (name: string, task: TaskFn) => TaskRegistry;
2846
- get: (name: string) => TaskFn | undefined;
2847
- tasks: () => Array<[string, TaskFn]>;
2848
- has: (name: string) => boolean;
2849
- names: () => string[];
2850
- size: () => number;
2851
- };
2852
-
2853
- function isTaskFnLike(candidate: unknown): candidate is TaskFn {
2854
- return (
2855
- typeof candidate === 'function' &&
2856
- !!candidate &&
2857
- 'task' in (candidate as Record<string, unknown>) &&
2858
- typeof (candidate as { task?: unknown }).task === 'object'
2859
- );
2860
- }
2861
-
2862
- export function createRegistry(): TaskRegistry {
2863
- const entries = new Map<string, TaskFn>();
2864
-
2865
- const registry: TaskRegistry = {
2866
- register(name: string, task: TaskFn) {
2867
- if (!name || typeof name !== 'string') {
2868
- throw new Error('Registry task name must be a non-empty string.');
2869
- }
2870
- if (!isTaskFnLike(task)) {
2871
- throw new Error(\`Registry task '\${name}' must be a TaskFn.\`);
2872
- }
2873
- if (entries.has(name)) {
2874
- throw new Error(\`Registry already has a task named '\${name}'.\`);
2875
- }
2876
- if (task.task) {
2877
- task.task.name = name;
2878
- }
2879
- entries.set(name, task);
2880
- return registry;
2881
- },
2882
- get(name: string) {
2883
- return entries.get(name);
2884
- },
2885
- tasks() {
2886
- return Array.from(entries.entries()).sort(([a], [b]) => a.localeCompare(b));
2887
- },
2888
- has(name: string) {
2889
- return entries.has(name);
2890
- },
2891
- names() {
2892
- return Array.from(entries.keys()).sort((a, b) => a.localeCompare(b));
2893
- },
2894
- size() {
2895
- return entries.size;
2896
- },
2897
- };
2898
-
2899
- return registry;
2900
- }
2901
- `;
2902
- var registryJsTemplate = `export function createRegistry() {
2903
- const entries = new Map();
2904
-
2905
- const registry = {
2906
- register(name, task) {
2907
- if (!name || typeof name !== 'string') {
2908
- throw new Error('Registry task name must be a non-empty string.');
2909
- }
2910
- if (!task || typeof task !== 'function' || !task.task) {
2911
- throw new Error(\`Registry task '\${name}' must be a TaskFn.\`);
2912
- }
2913
- if (entries.has(name)) {
2914
- throw new Error(\`Registry already has a task named '\${name}'.\`);
2915
- }
2916
- if (task.task) {
2917
- task.task.name = name;
2918
- }
2919
- entries.set(name, task);
2920
- return registry;
2921
- },
2922
- get(name) {
2923
- return entries.get(name);
2924
- },
2925
- tasks() {
2926
- return Array.from(entries.entries()).sort(([a], [b]) => a.localeCompare(b));
2927
- },
2928
- has(name) {
2929
- return entries.has(name);
2930
- },
2931
- names() {
2932
- return Array.from(entries.keys()).sort((a, b) => a.localeCompare(b));
2933
- },
2934
- size() {
2935
- return entries.size;
2936
- },
2937
- };
2938
-
2939
- return registry;
2940
- }
2941
- `;
2942
- var taskWrapperTsTemplate = `import { task as baseTask, type RunFn, type RunFnReturnValue, type TaskFn, type TaskOptions, type TaskParams } from 'hostctl';
2943
- import type { ZodTypeAny } from 'zod';
2944
-
2945
- export type TaskOptionsWithSchema = TaskOptions & {
2946
- inputSchema?: ZodTypeAny;
2947
- outputSchema?: ZodTypeAny;
2948
- };
2949
-
2950
- export function task<TParams extends TaskParams, TReturn extends RunFnReturnValue>(
2951
- runFn: RunFn<TParams, TReturn>,
2952
- options?: TaskOptionsWithSchema,
2953
- ): TaskFn<TParams, TReturn> {
2954
- const taskFn = baseTask(runFn, options as TaskOptions);
2955
- if (options?.inputSchema) {
2956
- (taskFn.task as any).inputSchema = options.inputSchema;
2957
- }
2958
- if (options?.outputSchema) {
2959
- (taskFn.task as any).outputSchema = options.outputSchema;
2960
- }
2961
- return taskFn;
2962
- }
2963
- `;
2964
- var taskWrapperJsTemplate = `import { task as baseTask } from 'hostctl';
2965
-
2966
- export function task(runFn, options) {
2967
- const taskFn = baseTask(runFn, options);
2968
- if (options?.inputSchema) {
2969
- taskFn.task.inputSchema = options.inputSchema;
2970
- }
2971
- if (options?.outputSchema) {
2972
- taskFn.task.outputSchema = options.outputSchema;
2973
- }
2974
- return taskFn;
2975
- }
2976
- `;
2977
- var sampleTaskTsTemplate = `import { task, type TaskContext } from '../task.js';
2978
- import { z } from 'zod';
2840
+ var sampleTaskTsTemplate = `import { task, type TaskContext, z } from 'hostctl';
2979
2841
 
2980
2842
  const HelloInputSchema = z.object({
2981
2843
  name: z.string().optional(),
@@ -3008,8 +2870,7 @@ export default task(run, {
3008
2870
  outputSchema: HelloOutputSchema,
3009
2871
  });
3010
2872
  `;
3011
- var sampleTaskJsTemplate = `import { task } from '../task.js';
3012
- import { z } from 'zod';
2873
+ var sampleTaskJsTemplate = `import { task, z } from 'hostctl';
3013
2874
 
3014
2875
  const HelloInputSchema = z.object({
3015
2876
  name: z.string().optional(),
@@ -3055,7 +2916,7 @@ hostctl run . ${registryPrefix}.hello name:Hostctl excited:true
3055
2916
  - Tasks live under \`src/\` and export a default \`task(...)\`.
3056
2917
  - \`src/index.(ts|js)\` re-exports tasks and publishes a registry for discovery.
3057
2918
  - Task names are unqualified; the registry assigns qualified names.
3058
- - Schema helpers come from \`zod\` and are attached to tasks for discovery.
2919
+ - Schema helpers come from \`hostctl\` (re-exported \`zod\`) and are attached to tasks for discovery.
3059
2920
 
3060
2921
  ## Publish
3061
2922
 
@@ -3093,8 +2954,6 @@ async function createPackage(packageName, options) {
3093
2954
  await fs5.writeFile(path3.join(packageDir, "tsconfig.json"), tsconfigTemplate);
3094
2955
  await fs5.mkdir(path3.join(packageDir, "src", "tasks"), { recursive: true });
3095
2956
  await fs5.writeFile(path3.join(packageDir, "src", "index.ts"), indexTsTemplate(registryPrefix));
3096
- await fs5.writeFile(path3.join(packageDir, "src", "registry.ts"), registryTsTemplate);
3097
- await fs5.writeFile(path3.join(packageDir, "src", "task.ts"), taskWrapperTsTemplate);
3098
2957
  await fs5.writeFile(path3.join(packageDir, "src", "tasks", "hello.ts"), sampleTaskTsTemplate);
3099
2958
  await fs5.writeFile(path3.join(packageDir, "README.md"), readmeTemplate(packageJsonName, registryPrefix, true));
3100
2959
  await fs5.writeFile(path3.join(packageDir, ".gitignore"), gitignoreTemplate);
@@ -3102,8 +2961,6 @@ async function createPackage(packageName, options) {
3102
2961
  await fs5.writeFile(path3.join(packageDir, "package.json"), packageJsonJsTemplate(packageJsonName));
3103
2962
  await fs5.mkdir(path3.join(packageDir, "src", "tasks"), { recursive: true });
3104
2963
  await fs5.writeFile(path3.join(packageDir, "src", "index.js"), indexJsTemplate(registryPrefix));
3105
- await fs5.writeFile(path3.join(packageDir, "src", "registry.js"), registryJsTemplate);
3106
- await fs5.writeFile(path3.join(packageDir, "src", "task.js"), taskWrapperJsTemplate);
3107
2964
  await fs5.writeFile(path3.join(packageDir, "src", "tasks", "hello.js"), sampleTaskJsTemplate);
3108
2965
  await fs5.writeFile(path3.join(packageDir, "README.md"), readmeTemplate(packageJsonName, registryPrefix, false));
3109
2966
  await fs5.writeFile(path3.join(packageDir, ".gitignore"), gitignoreTemplate);
@@ -3944,15 +3801,24 @@ var PackageManager = class {
3944
3801
  }
3945
3802
  async ensurePackagesDirHasPackageJson(packagesDir) {
3946
3803
  const packageJsonPath = packagesDir.join("package.json");
3947
- if (!await packageJsonPath.exists()) {
3948
- const packageJson = {
3804
+ const hostctlOverride = process.env.HOSTCTL_PKG_HOSTCTL_OVERRIDE;
3805
+ const overrideValue = hostctlOverride ? hostctlOverride.startsWith("file:") ? hostctlOverride : `file:${Path.new(hostctlOverride).absolute().toString()}` : void 0;
3806
+ let packageJson;
3807
+ if (await packageJsonPath.exists()) {
3808
+ const raw = await fs6.readFile(packageJsonPath.toString(), "utf-8");
3809
+ packageJson = JSON.parse(raw);
3810
+ } else {
3811
+ packageJson = {
3949
3812
  name: "hostctl-packages",
3950
3813
  version: "1.0.0",
3951
3814
  description: "Hostctl package management directory",
3952
3815
  private: true
3953
3816
  };
3954
- await fs6.writeFile(packageJsonPath.toString(), JSON.stringify(packageJson, null, 2));
3955
3817
  }
3818
+ if (overrideValue) {
3819
+ packageJson.overrides = { ...packageJson.overrides ?? {}, hostctl: overrideValue };
3820
+ }
3821
+ await fs6.writeFile(packageJsonPath.toString(), JSON.stringify(packageJson, null, 2));
3956
3822
  }
3957
3823
  // Scan node_modules for the real installed package (by name or repo match)
3958
3824
  async findRealInstalledNpmPackagePath(packagesDir, source) {
@@ -7437,23 +7303,24 @@ function isTaskFnLike2(candidate) {
7437
7303
  }
7438
7304
  function createRegistry() {
7439
7305
  const entries = /* @__PURE__ */ new Map();
7306
+ function register(name, task6) {
7307
+ if (!name || typeof name !== "string") {
7308
+ throw new Error("Registry task name must be a non-empty string.");
7309
+ }
7310
+ if (!isTaskFnLike2(task6)) {
7311
+ throw new Error(`Registry task '${name}' must be a TaskFn.`);
7312
+ }
7313
+ if (entries.has(name)) {
7314
+ throw new Error(`Registry already has a task named '${name}'.`);
7315
+ }
7316
+ if (task6.task) {
7317
+ task6.task.name = name;
7318
+ }
7319
+ entries.set(name, task6);
7320
+ return registry2;
7321
+ }
7440
7322
  const registry2 = {
7441
- register(name, task6) {
7442
- if (!name || typeof name !== "string") {
7443
- throw new Error("Registry task name must be a non-empty string.");
7444
- }
7445
- if (!isTaskFnLike2(task6)) {
7446
- throw new Error(`Registry task '${name}' must be a TaskFn.`);
7447
- }
7448
- if (entries.has(name)) {
7449
- throw new Error(`Registry already has a task named '${name}'.`);
7450
- }
7451
- if (task6.task) {
7452
- task6.task.name = name;
7453
- }
7454
- entries.set(name, task6);
7455
- return registry2;
7456
- },
7323
+ register,
7457
7324
  get(name) {
7458
7325
  return entries.get(name);
7459
7326
  },
@@ -33599,22 +33466,25 @@ function resolvedTaskName(task6) {
33599
33466
  }
33600
33467
  return deriveCoreTaskName(task6) ?? taskName ?? null;
33601
33468
  }
33602
- function collectTasksFrom(source, tasks, seen) {
33469
+ function collectTasksFrom(source, tasks, seen, pathParts = []) {
33603
33470
  if (!source) return;
33604
33471
  if (isTaskFnLike3(source)) {
33605
33472
  if (!seen.has(source)) {
33473
+ if (!hasExplicitName(source) && pathParts.length > 0) {
33474
+ source.task.name = `core.${pathParts.join(".")}`;
33475
+ }
33606
33476
  tasks.push(source);
33607
33477
  seen.add(source);
33608
33478
  }
33609
33479
  return;
33610
33480
  }
33611
33481
  if (Array.isArray(source)) {
33612
- source.forEach((entry) => collectTasksFrom(entry, tasks, seen));
33482
+ source.forEach((entry) => collectTasksFrom(entry, tasks, seen, pathParts));
33613
33483
  return;
33614
33484
  }
33615
33485
  if (isTraversableObject(source)) {
33616
- for (const value of Object.values(source)) {
33617
- collectTasksFrom(value, tasks, seen);
33486
+ for (const [key, value] of Object.entries(source)) {
33487
+ collectTasksFrom(value, tasks, seen, [...pathParts, key]);
33618
33488
  }
33619
33489
  }
33620
33490
  }