hostctl 0.1.55 → 0.1.58
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/bin/hostctl.js +436 -62
- package/dist/bin/hostctl.js.map +1 -1
- package/dist/index.d.ts +28 -2
- package/dist/index.js +436 -62
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -397,6 +397,24 @@ declare class InteractionHandler {
|
|
|
397
397
|
end(): void;
|
|
398
398
|
}
|
|
399
399
|
|
|
400
|
+
type CacheScope = 'global' | 'host' | 'invocation';
|
|
401
|
+
type CacheMode = 'use' | 'refresh' | 'bypass';
|
|
402
|
+
interface CacheOptions {
|
|
403
|
+
scope?: CacheScope;
|
|
404
|
+
key?: string;
|
|
405
|
+
ttlMs?: number;
|
|
406
|
+
mode?: CacheMode;
|
|
407
|
+
}
|
|
408
|
+
type CacheConfig = boolean | CacheScope | CacheOptions;
|
|
409
|
+
interface TaskRunOptions {
|
|
410
|
+
cache?: CacheConfig;
|
|
411
|
+
}
|
|
412
|
+
interface MemoizeOptions {
|
|
413
|
+
scope?: CacheScope;
|
|
414
|
+
ttlMs?: number;
|
|
415
|
+
mode?: CacheMode;
|
|
416
|
+
}
|
|
417
|
+
|
|
400
418
|
/**
|
|
401
419
|
* Generic object type for parameters and return values.
|
|
402
420
|
*/
|
|
@@ -471,7 +489,11 @@ interface TaskContext<TParams extends TaskParams = TaskParams> {
|
|
|
471
489
|
pty?: boolean;
|
|
472
490
|
}) => Promise<CommandResult>;
|
|
473
491
|
ssh: <TRemoteParams extends TaskParams = {}, TRemoteReturn extends RunFnReturnValue = RunFnReturnValue>(tags: string[], remoteTaskFn: (remoteContext: TaskContext<TRemoteParams>) => Promise<TRemoteReturn>) => Promise<Record<string, TRemoteReturn | Error>>;
|
|
474
|
-
run:
|
|
492
|
+
run: {
|
|
493
|
+
<TRunReturn extends RunFnReturnValue>(taskPartialFn: TaskPartialFn<TRunReturn>, options?: TaskRunOptions): Promise<TRunReturn>;
|
|
494
|
+
<TParams extends TaskParams, TRunReturn extends RunFnReturnValue>(taskFn: TaskFn<TParams, TRunReturn>, params?: TParams, options?: TaskRunOptions): Promise<TRunReturn>;
|
|
495
|
+
};
|
|
496
|
+
memoize: <TReturn>(key: string, valueOrFactory: TReturn | Promise<TReturn> | (() => TReturn | Promise<TReturn>), options?: MemoizeOptions) => Promise<TReturn>;
|
|
475
497
|
getPassword: () => Promise<string | undefined>;
|
|
476
498
|
getSecret: (name: string) => Promise<string | undefined>;
|
|
477
499
|
exit: (exitCode: number, message?: string) => void;
|
|
@@ -479,7 +501,10 @@ interface TaskContext<TParams extends TaskParams = TaskParams> {
|
|
|
479
501
|
selectedInventory: (tags?: string[]) => Host[];
|
|
480
502
|
file: FileSystemOperations$1;
|
|
481
503
|
}
|
|
482
|
-
type TaskPartialFn<TReturn extends RunFnReturnValue = RunFnReturnValue> = (parentInvocation: IInvocation) => Promise<TReturn
|
|
504
|
+
type TaskPartialFn<TReturn extends RunFnReturnValue = RunFnReturnValue> = ((parentInvocation: IInvocation) => Promise<TReturn>) & {
|
|
505
|
+
taskFn?: TaskFn<any, RunFnReturnValue>;
|
|
506
|
+
params?: TaskParams;
|
|
507
|
+
};
|
|
483
508
|
interface TaskFn<TParams extends TaskParams = TaskParams, TReturn extends RunFnReturnValue = RunFnReturnValue> {
|
|
484
509
|
(params?: TParams): TaskPartialFn<TReturn>;
|
|
485
510
|
task: Task<TParams, TReturn>;
|
|
@@ -748,6 +773,7 @@ declare class App {
|
|
|
748
773
|
private outputStyle;
|
|
749
774
|
private _tmpDir?;
|
|
750
775
|
private tmpFileRegistry;
|
|
776
|
+
private taskCache;
|
|
751
777
|
taskTree: TaskTree;
|
|
752
778
|
verbosity: VerbosityLevel;
|
|
753
779
|
private passwordProvider?;
|
package/dist/index.js
CHANGED
|
@@ -2719,7 +2719,7 @@ var Verbosity = {
|
|
|
2719
2719
|
};
|
|
2720
2720
|
|
|
2721
2721
|
// src/version.ts
|
|
2722
|
-
var version = "0.1.
|
|
2722
|
+
var version = "0.1.58";
|
|
2723
2723
|
|
|
2724
2724
|
// src/commands/pkg/create.ts
|
|
2725
2725
|
import { promises as fs5 } from "fs";
|
|
@@ -4150,9 +4150,13 @@ function task(runFn4, options) {
|
|
|
4150
4150
|
options?.outputSchema
|
|
4151
4151
|
);
|
|
4152
4152
|
const taskFnObject = function(params) {
|
|
4153
|
-
|
|
4154
|
-
|
|
4153
|
+
const normalizedParams = params ?? {};
|
|
4154
|
+
const taskPartialFn = function(parentInvocation) {
|
|
4155
|
+
return parentInvocation.invokeChildTask(taskFnObject, normalizedParams);
|
|
4155
4156
|
};
|
|
4157
|
+
taskPartialFn.taskFn = taskFnObject;
|
|
4158
|
+
taskPartialFn.params = normalizedParams;
|
|
4159
|
+
return taskPartialFn;
|
|
4156
4160
|
};
|
|
4157
4161
|
Object.assign(taskFnObject, { task: taskInstance });
|
|
4158
4162
|
return taskFnObject;
|
|
@@ -6154,58 +6158,64 @@ async function detectRockyLinux(exec) {
|
|
|
6154
6158
|
}
|
|
6155
6159
|
var os_default = task(
|
|
6156
6160
|
async function run26(context) {
|
|
6157
|
-
|
|
6158
|
-
|
|
6159
|
-
|
|
6160
|
-
|
|
6161
|
-
|
|
6162
|
-
|
|
6163
|
-
|
|
6164
|
-
|
|
6165
|
-
|
|
6166
|
-
|
|
6167
|
-
|
|
6168
|
-
|
|
6169
|
-
|
|
6170
|
-
|
|
6171
|
-
|
|
6172
|
-
|
|
6173
|
-
|
|
6174
|
-
|
|
6175
|
-
|
|
6176
|
-
|
|
6177
|
-
|
|
6178
|
-
|
|
6179
|
-
|
|
6180
|
-
|
|
6181
|
-
|
|
6182
|
-
|
|
6183
|
-
|
|
6184
|
-
|
|
6185
|
-
|
|
6186
|
-
|
|
6187
|
-
|
|
6188
|
-
|
|
6189
|
-
variant
|
|
6161
|
+
return await context.memoize(
|
|
6162
|
+
"core.host.os:v1",
|
|
6163
|
+
async () => {
|
|
6164
|
+
try {
|
|
6165
|
+
const { exec } = context;
|
|
6166
|
+
const {
|
|
6167
|
+
success: ostypeSuccess,
|
|
6168
|
+
stdout: ostypeOutput,
|
|
6169
|
+
stderr: ostypeStderr
|
|
6170
|
+
} = await exec(["bash", "-c", "echo $OSTYPE"]);
|
|
6171
|
+
if (!ostypeSuccess) {
|
|
6172
|
+
throw new Error(`Failed to get OSTYPE: ${ostypeStderr}`);
|
|
6173
|
+
}
|
|
6174
|
+
const family = await match3(ostypeOutput.trim().toLowerCase()).with(P2.string.startsWith("solaris"), () => "solaris").with(P2.string.startsWith("darwin"), () => "darwin").with(P2.string.startsWith("linux"), () => "linux").with(P2.string.startsWith("bsd"), () => "bsd").with(P2.string.startsWith("freebsd"), () => "bsd").with(P2.string.startsWith("msys"), () => "windows").with(P2.string.startsWith("cygwin"), () => "windows").with(P2.string.startsWith("mingw"), () => "windows").otherwise(async () => {
|
|
6175
|
+
const { stdout: unameOutput } = await exec(["uname"]);
|
|
6176
|
+
const unameFamily = match3(unameOutput.trim().toLowerCase()).with(P2.string.startsWith("sunos"), () => "solaris").with(P2.string.startsWith("darwin"), () => "darwin").with(P2.string.startsWith("linux"), () => "linux").with(P2.string.startsWith("freebsd"), () => "bsd").with(P2.string.startsWith("openbsd"), () => "bsd").with(P2.string.startsWith("netbsd"), () => "bsd").otherwise(() => "unknown");
|
|
6177
|
+
return unameFamily;
|
|
6178
|
+
});
|
|
6179
|
+
const [osIdLike, osId, osVersion] = await match3(family).with("bsd", async () => {
|
|
6180
|
+
const { stdout: unameROutput } = await exec(["uname", "-r"]);
|
|
6181
|
+
return [family, family, unameROutput.trim()];
|
|
6182
|
+
}).with("darwin", async () => {
|
|
6183
|
+
const { stdout: swVersOutput } = await exec(["sw_vers", "-productVersion"]);
|
|
6184
|
+
return [family, family, swVersOutput.trim()];
|
|
6185
|
+
}).with("linux", async () => {
|
|
6186
|
+
const { idLike, id, version: version2 } = await getOsReleaseInfo(exec);
|
|
6187
|
+
return [idLike, id, version2];
|
|
6188
|
+
}).with("solaris", async () => {
|
|
6189
|
+
const { stdout: unameROutput } = await exec(["uname", "-r"]);
|
|
6190
|
+
return ["solaris", "solaris", unameROutput.trim()];
|
|
6191
|
+
}).with("windows", () => ["windows", "windows", "unknown"]).otherwise(() => ["unknown", "unknown", "unknown"]);
|
|
6192
|
+
let variant = osId.toLowerCase();
|
|
6193
|
+
if (family === "linux" && !variant.includes("rocky") && (osIdLike.toLowerCase().includes("rocky") || osId.toLowerCase().includes("rhel"))) {
|
|
6194
|
+
const isRocky = await detectRockyLinux(exec);
|
|
6195
|
+
if (isRocky) {
|
|
6196
|
+
variant = "rocky";
|
|
6197
|
+
}
|
|
6198
|
+
}
|
|
6199
|
+
return {
|
|
6200
|
+
success: true,
|
|
6201
|
+
family,
|
|
6202
|
+
os: osIdLike.toLowerCase(),
|
|
6203
|
+
variant,
|
|
6204
|
+
version: osVersion
|
|
6205
|
+
};
|
|
6206
|
+
} catch (error) {
|
|
6207
|
+
return {
|
|
6208
|
+
success: false,
|
|
6209
|
+
error: error.message,
|
|
6210
|
+
family: "unknown",
|
|
6211
|
+
os: "unknown",
|
|
6212
|
+
variant: "unknown",
|
|
6213
|
+
version: "unknown"
|
|
6214
|
+
};
|
|
6190
6215
|
}
|
|
6191
|
-
}
|
|
6192
|
-
|
|
6193
|
-
|
|
6194
|
-
family,
|
|
6195
|
-
os: osIdLike.toLowerCase(),
|
|
6196
|
-
variant,
|
|
6197
|
-
version: osVersion
|
|
6198
|
-
};
|
|
6199
|
-
} catch (error) {
|
|
6200
|
-
return {
|
|
6201
|
-
success: false,
|
|
6202
|
-
error: error.message,
|
|
6203
|
-
family: "unknown",
|
|
6204
|
-
os: "unknown",
|
|
6205
|
-
variant: "unknown",
|
|
6206
|
-
version: "unknown"
|
|
6207
|
-
};
|
|
6208
|
-
}
|
|
6216
|
+
},
|
|
6217
|
+
{ scope: "host" }
|
|
6218
|
+
);
|
|
6209
6219
|
},
|
|
6210
6220
|
{
|
|
6211
6221
|
name: "os",
|
|
@@ -31924,8 +31934,8 @@ function renderTaskRows(rows, {
|
|
|
31924
31934
|
name: r.name,
|
|
31925
31935
|
description: r.description,
|
|
31926
31936
|
module: r.module,
|
|
31927
|
-
inputSchema:
|
|
31928
|
-
outputSchema:
|
|
31937
|
+
inputSchema: schemaToDescriptor(r.inputSchema),
|
|
31938
|
+
outputSchema: schemaToDescriptor(r.outputSchema),
|
|
31929
31939
|
schemaSource: r.schemaSource
|
|
31930
31940
|
}));
|
|
31931
31941
|
console.log(JSON.stringify(sanitized, null, 2));
|
|
@@ -31966,9 +31976,12 @@ function summarizeType(val) {
|
|
|
31966
31976
|
case "optional":
|
|
31967
31977
|
case "ZodNullable":
|
|
31968
31978
|
case "nullable":
|
|
31979
|
+
case "ZodDefault":
|
|
31980
|
+
case "default":
|
|
31969
31981
|
return summarizeType(def.innerType || def.type);
|
|
31970
31982
|
case "ZodEffects":
|
|
31971
|
-
|
|
31983
|
+
case "pipe":
|
|
31984
|
+
return summarizeType(def.schema || def.in);
|
|
31972
31985
|
case "ZodString":
|
|
31973
31986
|
case "string":
|
|
31974
31987
|
return { type: "string", desc };
|
|
@@ -31991,8 +32004,11 @@ function summarizeType(val) {
|
|
|
31991
32004
|
return types.length ? { type: types.join(" | "), desc } : {};
|
|
31992
32005
|
}
|
|
31993
32006
|
case "ZodLiteral":
|
|
31994
|
-
case "literal":
|
|
31995
|
-
|
|
32007
|
+
case "literal": {
|
|
32008
|
+
const values3 = Array.isArray(def?.values) ? def.values : def?.value !== void 0 ? [def.value] : [];
|
|
32009
|
+
const value = values3.length ? values3[0] : void 0;
|
|
32010
|
+
return { type: value !== void 0 ? JSON.stringify(value) : void 0, desc };
|
|
32011
|
+
}
|
|
31996
32012
|
case "ZodObject":
|
|
31997
32013
|
case "object":
|
|
31998
32014
|
return { type: "object", desc };
|
|
@@ -32000,6 +32016,140 @@ function summarizeType(val) {
|
|
|
32000
32016
|
return { type: String(tn).replace(/^Zod/, "").toLowerCase(), desc };
|
|
32001
32017
|
}
|
|
32002
32018
|
}
|
|
32019
|
+
function schemaToDescriptor(schema) {
|
|
32020
|
+
if (!schema) return null;
|
|
32021
|
+
const { schema: base, nullable } = unwrapSchema(schema);
|
|
32022
|
+
const def = base?._def ?? base?.def;
|
|
32023
|
+
const typeName = def?.typeName || def?.type;
|
|
32024
|
+
const description = def?.description || base?.description;
|
|
32025
|
+
let descriptor = {};
|
|
32026
|
+
switch (typeName) {
|
|
32027
|
+
case "ZodString":
|
|
32028
|
+
case "string":
|
|
32029
|
+
descriptor.type = "string";
|
|
32030
|
+
break;
|
|
32031
|
+
case "ZodNumber":
|
|
32032
|
+
case "number":
|
|
32033
|
+
descriptor.type = "number";
|
|
32034
|
+
break;
|
|
32035
|
+
case "ZodBoolean":
|
|
32036
|
+
case "boolean":
|
|
32037
|
+
descriptor.type = "boolean";
|
|
32038
|
+
break;
|
|
32039
|
+
case "ZodArray":
|
|
32040
|
+
case "array": {
|
|
32041
|
+
const inner = def?.element ?? def?.type;
|
|
32042
|
+
descriptor.type = "array";
|
|
32043
|
+
descriptor.items = schemaToDescriptor(inner);
|
|
32044
|
+
break;
|
|
32045
|
+
}
|
|
32046
|
+
case "ZodObject":
|
|
32047
|
+
case "object": {
|
|
32048
|
+
const shapeSource = def?.shape || def?.shape_;
|
|
32049
|
+
const shape = shapeSource && typeof shapeSource === "function" ? shapeSource.call(def) : shapeSource;
|
|
32050
|
+
const properties = {};
|
|
32051
|
+
const required = [];
|
|
32052
|
+
if (shape && typeof shape === "object") {
|
|
32053
|
+
for (const [key, value] of Object.entries(shape)) {
|
|
32054
|
+
const { schema: fieldSchema, optional, nullable: fieldNullable } = unwrapSchema(value);
|
|
32055
|
+
const fieldDescriptor = schemaToDescriptor(fieldSchema) ?? {};
|
|
32056
|
+
if (fieldNullable) {
|
|
32057
|
+
fieldDescriptor.nullable = true;
|
|
32058
|
+
}
|
|
32059
|
+
properties[key] = fieldDescriptor;
|
|
32060
|
+
if (!optional) {
|
|
32061
|
+
required.push(key);
|
|
32062
|
+
}
|
|
32063
|
+
}
|
|
32064
|
+
}
|
|
32065
|
+
descriptor.type = "object";
|
|
32066
|
+
descriptor.properties = properties;
|
|
32067
|
+
descriptor.required = required.length ? required : void 0;
|
|
32068
|
+
break;
|
|
32069
|
+
}
|
|
32070
|
+
case "ZodUnion":
|
|
32071
|
+
case "union": {
|
|
32072
|
+
const options = def?.options ?? [];
|
|
32073
|
+
descriptor.anyOf = options.map((option) => schemaToDescriptor(option)).filter(Boolean);
|
|
32074
|
+
break;
|
|
32075
|
+
}
|
|
32076
|
+
case "ZodLiteral":
|
|
32077
|
+
case "literal": {
|
|
32078
|
+
const values3 = Array.isArray(def?.values) ? def.values : def?.value !== void 0 ? [def.value] : [];
|
|
32079
|
+
const sample = values3.length ? values3[0] : void 0;
|
|
32080
|
+
descriptor.type = sample === null ? "null" : typeof sample;
|
|
32081
|
+
descriptor.enum = values3;
|
|
32082
|
+
break;
|
|
32083
|
+
}
|
|
32084
|
+
case "ZodEnum":
|
|
32085
|
+
case "enum":
|
|
32086
|
+
const enumValues = Array.isArray(def?.values) ? def.values : Object.values(def?.entries ?? {});
|
|
32087
|
+
descriptor.enum = enumValues;
|
|
32088
|
+
if (enumValues.length) {
|
|
32089
|
+
const hasNumber = enumValues.some((value) => typeof value === "number");
|
|
32090
|
+
const hasString = enumValues.some((value) => typeof value === "string");
|
|
32091
|
+
descriptor.type = hasNumber && !hasString ? "number" : "string";
|
|
32092
|
+
} else {
|
|
32093
|
+
descriptor.type = "string";
|
|
32094
|
+
}
|
|
32095
|
+
break;
|
|
32096
|
+
case "ZodNativeEnum":
|
|
32097
|
+
case "nativeenum": {
|
|
32098
|
+
const values3 = Array.isArray(def?.values) ? def.values : Object.values(def?.entries ?? def?.values ?? {});
|
|
32099
|
+
descriptor.type = values3.every((value) => typeof value === "number") ? "number" : "string";
|
|
32100
|
+
descriptor.enum = values3;
|
|
32101
|
+
break;
|
|
32102
|
+
}
|
|
32103
|
+
default:
|
|
32104
|
+
descriptor.type = typeName ? String(typeName).replace(/^Zod/, "").toLowerCase() : "unknown";
|
|
32105
|
+
break;
|
|
32106
|
+
}
|
|
32107
|
+
if (description) {
|
|
32108
|
+
descriptor.description = description;
|
|
32109
|
+
}
|
|
32110
|
+
if (nullable) {
|
|
32111
|
+
descriptor.nullable = true;
|
|
32112
|
+
}
|
|
32113
|
+
return descriptor;
|
|
32114
|
+
}
|
|
32115
|
+
function unwrapSchema(schema) {
|
|
32116
|
+
let current = schema;
|
|
32117
|
+
let optional = false;
|
|
32118
|
+
let nullable = false;
|
|
32119
|
+
while (current) {
|
|
32120
|
+
const def = current?._def ?? current?.def;
|
|
32121
|
+
const typeName = def?.typeName || def?.type;
|
|
32122
|
+
if (typeName === "ZodOptional" || typeName === "optional") {
|
|
32123
|
+
optional = true;
|
|
32124
|
+
current = def?.innerType ?? def?.type;
|
|
32125
|
+
continue;
|
|
32126
|
+
}
|
|
32127
|
+
if (typeName === "ZodNullable" || typeName === "nullable") {
|
|
32128
|
+
nullable = true;
|
|
32129
|
+
current = def?.innerType ?? def?.type;
|
|
32130
|
+
continue;
|
|
32131
|
+
}
|
|
32132
|
+
if (typeName === "ZodDefault" || typeName === "default") {
|
|
32133
|
+
optional = true;
|
|
32134
|
+
current = def?.innerType ?? def?.type;
|
|
32135
|
+
continue;
|
|
32136
|
+
}
|
|
32137
|
+
if (typeName === "ZodEffects" || typeName === "effects") {
|
|
32138
|
+
current = def?.schema;
|
|
32139
|
+
continue;
|
|
32140
|
+
}
|
|
32141
|
+
if (typeName === "pipe") {
|
|
32142
|
+
current = def?.in ?? def?.schema;
|
|
32143
|
+
continue;
|
|
32144
|
+
}
|
|
32145
|
+
if (typeName === "transform") {
|
|
32146
|
+
current = def?.schema ?? def?.in;
|
|
32147
|
+
continue;
|
|
32148
|
+
}
|
|
32149
|
+
break;
|
|
32150
|
+
}
|
|
32151
|
+
return { schema: current, optional, nullable };
|
|
32152
|
+
}
|
|
32003
32153
|
function formatSchemaLines(schema) {
|
|
32004
32154
|
if (!schema) return ["none"];
|
|
32005
32155
|
const def = schema._def ?? schema.def;
|
|
@@ -32012,7 +32162,7 @@ function formatSchemaLines(schema) {
|
|
|
32012
32162
|
const tn = anyVal?._def?.typeName || anyVal?.def?.type;
|
|
32013
32163
|
const typeString = summary2.type || (tn ? String(tn).replace(/^Zod/, "").toLowerCase() : void 0);
|
|
32014
32164
|
const desc = summary2.desc;
|
|
32015
|
-
const optional =
|
|
32165
|
+
const optional = ["ZodOptional", "optional", "ZodNullable", "nullable", "ZodDefault", "default"].includes(tn);
|
|
32016
32166
|
const typePart = typeString ? `: ${typeString}` : "";
|
|
32017
32167
|
const descPart = desc ? ` - ${desc}` : "";
|
|
32018
32168
|
return `- ${key}${optional ? "?" : ""}${typePart}${descPart}`;
|
|
@@ -32792,6 +32942,9 @@ import AdmZip from "adm-zip";
|
|
|
32792
32942
|
|
|
32793
32943
|
// src/hash.ts
|
|
32794
32944
|
import { createHash } from "crypto";
|
|
32945
|
+
function sha256(str) {
|
|
32946
|
+
return createHash("sha256").update(str).digest("hex");
|
|
32947
|
+
}
|
|
32795
32948
|
|
|
32796
32949
|
// src/param-map.ts
|
|
32797
32950
|
import { match as match5 } from "ts-pattern";
|
|
@@ -32935,6 +33088,154 @@ var runAllRemote_default = task(run252, {
|
|
|
32935
33088
|
outputSchema: RunResultSchema
|
|
32936
33089
|
});
|
|
32937
33090
|
|
|
33091
|
+
// src/task-cache.ts
|
|
33092
|
+
var DEFAULT_CACHE_MODE = "use";
|
|
33093
|
+
var LOCAL_HOST_SCOPE_KEY = "host:local";
|
|
33094
|
+
function normalizeValue(value) {
|
|
33095
|
+
if (value === void 0) {
|
|
33096
|
+
return void 0;
|
|
33097
|
+
}
|
|
33098
|
+
if (value === null) {
|
|
33099
|
+
return null;
|
|
33100
|
+
}
|
|
33101
|
+
if (typeof value === "bigint") {
|
|
33102
|
+
return value.toString();
|
|
33103
|
+
}
|
|
33104
|
+
if (value instanceof Date) {
|
|
33105
|
+
return value.toISOString();
|
|
33106
|
+
}
|
|
33107
|
+
if (Array.isArray(value)) {
|
|
33108
|
+
return value.map((item) => {
|
|
33109
|
+
const normalized = normalizeValue(item);
|
|
33110
|
+
return normalized === void 0 ? null : normalized;
|
|
33111
|
+
});
|
|
33112
|
+
}
|
|
33113
|
+
if (typeof value === "object") {
|
|
33114
|
+
const obj = value;
|
|
33115
|
+
const sortedKeys = Object.keys(obj).sort();
|
|
33116
|
+
const result = {};
|
|
33117
|
+
for (const key of sortedKeys) {
|
|
33118
|
+
const normalized = normalizeValue(obj[key]);
|
|
33119
|
+
if (normalized !== void 0) {
|
|
33120
|
+
result[key] = normalized;
|
|
33121
|
+
}
|
|
33122
|
+
}
|
|
33123
|
+
return result;
|
|
33124
|
+
}
|
|
33125
|
+
return value;
|
|
33126
|
+
}
|
|
33127
|
+
function stableJson(value) {
|
|
33128
|
+
const normalized = normalizeValue(value);
|
|
33129
|
+
return JSON.stringify(normalized ?? null);
|
|
33130
|
+
}
|
|
33131
|
+
function normalizeCacheConfig(cache) {
|
|
33132
|
+
if (!cache) {
|
|
33133
|
+
return { enabled: false, mode: DEFAULT_CACHE_MODE };
|
|
33134
|
+
}
|
|
33135
|
+
if (cache === true) {
|
|
33136
|
+
return { enabled: true, mode: DEFAULT_CACHE_MODE };
|
|
33137
|
+
}
|
|
33138
|
+
if (typeof cache === "string") {
|
|
33139
|
+
return { enabled: true, scope: cache, mode: DEFAULT_CACHE_MODE };
|
|
33140
|
+
}
|
|
33141
|
+
return {
|
|
33142
|
+
enabled: true,
|
|
33143
|
+
scope: cache.scope,
|
|
33144
|
+
key: cache.key,
|
|
33145
|
+
ttlMs: cache.ttlMs,
|
|
33146
|
+
mode: cache.mode ?? DEFAULT_CACHE_MODE
|
|
33147
|
+
};
|
|
33148
|
+
}
|
|
33149
|
+
function buildTaskCacheKey(taskIdentity, params) {
|
|
33150
|
+
const taskName = taskIdentity?.task?.name;
|
|
33151
|
+
const modulePath = taskIdentity?.task?.taskModuleAbsolutePath;
|
|
33152
|
+
const identity2 = taskName ?? (modulePath ? Path.new(modulePath).absolute().toString() : "unknown-task");
|
|
33153
|
+
const paramsHash = sha256(stableJson(params ?? {}));
|
|
33154
|
+
return `${identity2}:${paramsHash}`;
|
|
33155
|
+
}
|
|
33156
|
+
function buildHostScopeKey(host, configRef) {
|
|
33157
|
+
if (!host) {
|
|
33158
|
+
return LOCAL_HOST_SCOPE_KEY;
|
|
33159
|
+
}
|
|
33160
|
+
const identity2 = {
|
|
33161
|
+
alias: host.alias ?? "",
|
|
33162
|
+
hostname: host.hostname ?? "",
|
|
33163
|
+
user: host.user ?? "",
|
|
33164
|
+
port: host.port ?? 22,
|
|
33165
|
+
config: configRef ?? ""
|
|
33166
|
+
};
|
|
33167
|
+
return `host:${sha256(stableJson(identity2))}`;
|
|
33168
|
+
}
|
|
33169
|
+
function buildInvocationScopeKey(rootInvocationId) {
|
|
33170
|
+
return `invocation:${rootInvocationId}`;
|
|
33171
|
+
}
|
|
33172
|
+
var TaskCacheStore = class {
|
|
33173
|
+
entries = /* @__PURE__ */ new Map();
|
|
33174
|
+
runId;
|
|
33175
|
+
constructor(runId) {
|
|
33176
|
+
this.runId = runId ?? crypto.randomUUID();
|
|
33177
|
+
}
|
|
33178
|
+
globalScopeKey() {
|
|
33179
|
+
return `global:${this.runId}`;
|
|
33180
|
+
}
|
|
33181
|
+
get(scopeKey, cacheKey2) {
|
|
33182
|
+
const scope = this.entries.get(scopeKey);
|
|
33183
|
+
if (!scope) {
|
|
33184
|
+
return void 0;
|
|
33185
|
+
}
|
|
33186
|
+
const entry = scope.get(cacheKey2);
|
|
33187
|
+
if (!entry) {
|
|
33188
|
+
return void 0;
|
|
33189
|
+
}
|
|
33190
|
+
if (entry.expiresAt > 0 && Date.now() > entry.expiresAt) {
|
|
33191
|
+
scope.delete(cacheKey2);
|
|
33192
|
+
if (scope.size === 0) {
|
|
33193
|
+
this.entries.delete(scopeKey);
|
|
33194
|
+
}
|
|
33195
|
+
return void 0;
|
|
33196
|
+
}
|
|
33197
|
+
return entry;
|
|
33198
|
+
}
|
|
33199
|
+
set(scopeKey, cacheKey2, value, ttlMs) {
|
|
33200
|
+
const scope = this.entries.get(scopeKey) ?? /* @__PURE__ */ new Map();
|
|
33201
|
+
const expiresAt = ttlMs && ttlMs > 0 ? Date.now() + ttlMs : 0;
|
|
33202
|
+
const entry = { value, expiresAt };
|
|
33203
|
+
scope.set(cacheKey2, entry);
|
|
33204
|
+
this.entries.set(scopeKey, scope);
|
|
33205
|
+
return entry;
|
|
33206
|
+
}
|
|
33207
|
+
delete(scopeKey, cacheKey2) {
|
|
33208
|
+
const scope = this.entries.get(scopeKey);
|
|
33209
|
+
if (!scope) {
|
|
33210
|
+
return;
|
|
33211
|
+
}
|
|
33212
|
+
scope.delete(cacheKey2);
|
|
33213
|
+
if (scope.size === 0) {
|
|
33214
|
+
this.entries.delete(scopeKey);
|
|
33215
|
+
}
|
|
33216
|
+
}
|
|
33217
|
+
async resolve(scopeKey, cacheKey2, compute, options = {}) {
|
|
33218
|
+
const mode = options.mode ?? DEFAULT_CACHE_MODE;
|
|
33219
|
+
if (mode === "bypass") {
|
|
33220
|
+
return await compute();
|
|
33221
|
+
}
|
|
33222
|
+
if (mode === "use") {
|
|
33223
|
+
const cached = this.get(scopeKey, cacheKey2);
|
|
33224
|
+
if (cached) {
|
|
33225
|
+
return await cached.value;
|
|
33226
|
+
}
|
|
33227
|
+
}
|
|
33228
|
+
const promise = Promise.resolve().then(compute);
|
|
33229
|
+
this.set(scopeKey, cacheKey2, promise, options.ttlMs);
|
|
33230
|
+
try {
|
|
33231
|
+
return await promise;
|
|
33232
|
+
} catch (error) {
|
|
33233
|
+
this.delete(scopeKey, cacheKey2);
|
|
33234
|
+
throw error;
|
|
33235
|
+
}
|
|
33236
|
+
}
|
|
33237
|
+
};
|
|
33238
|
+
|
|
32938
33239
|
// src/app.ts
|
|
32939
33240
|
var TaskTree = class {
|
|
32940
33241
|
// private taskEventBus: Emittery<{ newTask: NewTaskEvent; taskComplete: TaskCompleteEvent }>;
|
|
@@ -33031,6 +33332,7 @@ var App6 = class _App {
|
|
|
33031
33332
|
outputStyle;
|
|
33032
33333
|
_tmpDir;
|
|
33033
33334
|
tmpFileRegistry;
|
|
33335
|
+
taskCache;
|
|
33034
33336
|
taskTree;
|
|
33035
33337
|
verbosity = Verbosity.ERROR;
|
|
33036
33338
|
passwordProvider;
|
|
@@ -33040,6 +33342,7 @@ var App6 = class _App {
|
|
|
33040
33342
|
this.taskTree = new TaskTree();
|
|
33041
33343
|
this.outputStyle = "plain";
|
|
33042
33344
|
this.tmpFileRegistry = new TmpFileRegistry(this.hostctlTmpDir());
|
|
33345
|
+
this.taskCache = new TaskCacheStore();
|
|
33043
33346
|
this.configRef = void 0;
|
|
33044
33347
|
this.hostSelector = void 0;
|
|
33045
33348
|
process4.on("exit", (code) => this.appExitCallback());
|
|
@@ -33355,6 +33658,27 @@ ${cmdRes.stderr.trim()}`));
|
|
|
33355
33658
|
}
|
|
33356
33659
|
taskContextForRunFn(invocation, params, hostForContext) {
|
|
33357
33660
|
const effectiveHost = hostForContext || invocation.host;
|
|
33661
|
+
const rootInvocationId = (() => {
|
|
33662
|
+
let current = invocation;
|
|
33663
|
+
while (current.parent) {
|
|
33664
|
+
current = current.parent;
|
|
33665
|
+
}
|
|
33666
|
+
return current.id;
|
|
33667
|
+
})();
|
|
33668
|
+
const defaultScope = () => effectiveHost ? "host" : "global";
|
|
33669
|
+
const scopeKeyFor = (scope) => {
|
|
33670
|
+
switch (scope) {
|
|
33671
|
+
case "global":
|
|
33672
|
+
return this.taskCache.globalScopeKey();
|
|
33673
|
+
case "host":
|
|
33674
|
+
return buildHostScopeKey(effectiveHost, this.configRef);
|
|
33675
|
+
case "invocation":
|
|
33676
|
+
return buildInvocationScopeKey(rootInvocationId);
|
|
33677
|
+
}
|
|
33678
|
+
};
|
|
33679
|
+
const isTaskFn = (candidate) => {
|
|
33680
|
+
return typeof candidate === "function" && !!candidate.task;
|
|
33681
|
+
};
|
|
33358
33682
|
return {
|
|
33359
33683
|
// Properties from TaskContext
|
|
33360
33684
|
params,
|
|
@@ -33384,8 +33708,58 @@ ${cmdRes.stderr.trim()}`));
|
|
|
33384
33708
|
ssh: async (tags, remoteTaskFn) => {
|
|
33385
33709
|
return await invocation.ssh(tags, remoteTaskFn);
|
|
33386
33710
|
},
|
|
33387
|
-
run: async (
|
|
33388
|
-
|
|
33711
|
+
run: async (...args) => {
|
|
33712
|
+
const [firstArg, secondArg, thirdArg] = args;
|
|
33713
|
+
let taskPartialFn;
|
|
33714
|
+
let taskIdentity;
|
|
33715
|
+
let paramsForKey;
|
|
33716
|
+
let options;
|
|
33717
|
+
if (isTaskFn(firstArg)) {
|
|
33718
|
+
taskIdentity = firstArg;
|
|
33719
|
+
paramsForKey = secondArg ?? {};
|
|
33720
|
+
options = thirdArg;
|
|
33721
|
+
taskPartialFn = taskIdentity(paramsForKey);
|
|
33722
|
+
} else {
|
|
33723
|
+
taskPartialFn = firstArg;
|
|
33724
|
+
options = secondArg;
|
|
33725
|
+
const meta = taskPartialFn;
|
|
33726
|
+
taskIdentity = meta.taskFn;
|
|
33727
|
+
paramsForKey = meta.params;
|
|
33728
|
+
}
|
|
33729
|
+
const cacheDecision = normalizeCacheConfig(options?.cache);
|
|
33730
|
+
if (!cacheDecision.enabled || cacheDecision.mode === "bypass") {
|
|
33731
|
+
return await invocation.run(taskPartialFn);
|
|
33732
|
+
}
|
|
33733
|
+
const scope = cacheDecision.scope ?? defaultScope();
|
|
33734
|
+
const cacheKey2 = cacheDecision.key ?? buildTaskCacheKey(taskIdentity, paramsForKey ?? {});
|
|
33735
|
+
const scopeKey = scopeKeyFor(scope);
|
|
33736
|
+
return await this.taskCache.resolve(scopeKey, cacheKey2, () => invocation.run(taskPartialFn), {
|
|
33737
|
+
ttlMs: cacheDecision.ttlMs,
|
|
33738
|
+
mode: cacheDecision.mode
|
|
33739
|
+
});
|
|
33740
|
+
},
|
|
33741
|
+
memoize: async (key, valueOrFactory, options) => {
|
|
33742
|
+
const cacheDecision = normalizeCacheConfig({
|
|
33743
|
+
scope: options?.scope,
|
|
33744
|
+
ttlMs: options?.ttlMs,
|
|
33745
|
+
mode: options?.mode,
|
|
33746
|
+
key
|
|
33747
|
+
});
|
|
33748
|
+
const scope = cacheDecision.scope ?? defaultScope();
|
|
33749
|
+
const scopeKey = scopeKeyFor(scope);
|
|
33750
|
+
const compute = async () => {
|
|
33751
|
+
if (typeof valueOrFactory === "function") {
|
|
33752
|
+
return await valueOrFactory();
|
|
33753
|
+
}
|
|
33754
|
+
return await valueOrFactory;
|
|
33755
|
+
};
|
|
33756
|
+
if (cacheDecision.mode === "bypass") {
|
|
33757
|
+
return await compute();
|
|
33758
|
+
}
|
|
33759
|
+
return await this.taskCache.resolve(scopeKey, key, compute, {
|
|
33760
|
+
ttlMs: cacheDecision.ttlMs,
|
|
33761
|
+
mode: cacheDecision.mode
|
|
33762
|
+
});
|
|
33389
33763
|
},
|
|
33390
33764
|
getPassword: async () => {
|
|
33391
33765
|
return await invocation.getPassword();
|