morphix-env 0.8.0 → 0.10.0
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/README.md +49 -1
- package/dist/SKILL.md +7 -0
- package/dist/cli.js +888 -105
- package/dist/index.d.ts +8 -0
- package/dist/index.js +32 -10
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -831,8 +831,8 @@ var require_react_development = __commonJS({
|
|
|
831
831
|
function lazyInitializer(payload) {
|
|
832
832
|
if (-1 === payload._status) {
|
|
833
833
|
var resolveDebugValue = null, rejectDebugValue = null, ioInfo = payload._ioInfo;
|
|
834
|
-
null != ioInfo && (ioInfo.start = ioInfo.end = performance.now(), ioInfo.value = new Promise(function(
|
|
835
|
-
resolveDebugValue =
|
|
834
|
+
null != ioInfo && (ioInfo.start = ioInfo.end = performance.now(), ioInfo.value = new Promise(function(resolve8, reject) {
|
|
835
|
+
resolveDebugValue = resolve8;
|
|
836
836
|
rejectDebugValue = reject;
|
|
837
837
|
}));
|
|
838
838
|
ioInfo = payload._result;
|
|
@@ -948,21 +948,21 @@ var require_react_development = __commonJS({
|
|
|
948
948
|
);
|
|
949
949
|
actScopeDepth = prevActScopeDepth;
|
|
950
950
|
}
|
|
951
|
-
function recursivelyFlushAsyncActWork(returnValue,
|
|
951
|
+
function recursivelyFlushAsyncActWork(returnValue, resolve8, reject) {
|
|
952
952
|
var queue = ReactSharedInternals.actQueue;
|
|
953
953
|
if (null !== queue)
|
|
954
954
|
if (0 !== queue.length)
|
|
955
955
|
try {
|
|
956
956
|
flushActQueue(queue);
|
|
957
957
|
enqueueTask(function() {
|
|
958
|
-
return recursivelyFlushAsyncActWork(returnValue,
|
|
958
|
+
return recursivelyFlushAsyncActWork(returnValue, resolve8, reject);
|
|
959
959
|
});
|
|
960
960
|
return;
|
|
961
961
|
} catch (error) {
|
|
962
962
|
ReactSharedInternals.thrownErrors.push(error);
|
|
963
963
|
}
|
|
964
964
|
else ReactSharedInternals.actQueue = null;
|
|
965
|
-
0 < ReactSharedInternals.thrownErrors.length ? (queue = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, reject(queue)) :
|
|
965
|
+
0 < ReactSharedInternals.thrownErrors.length ? (queue = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, reject(queue)) : resolve8(returnValue);
|
|
966
966
|
}
|
|
967
967
|
function flushActQueue(queue) {
|
|
968
968
|
if (!isFlushing) {
|
|
@@ -1150,7 +1150,7 @@ var require_react_development = __commonJS({
|
|
|
1150
1150
|
));
|
|
1151
1151
|
});
|
|
1152
1152
|
return {
|
|
1153
|
-
then: function(
|
|
1153
|
+
then: function(resolve8, reject) {
|
|
1154
1154
|
didAwaitActCall = true;
|
|
1155
1155
|
thenable.then(
|
|
1156
1156
|
function(returnValue) {
|
|
@@ -1160,7 +1160,7 @@ var require_react_development = __commonJS({
|
|
|
1160
1160
|
flushActQueue(queue), enqueueTask(function() {
|
|
1161
1161
|
return recursivelyFlushAsyncActWork(
|
|
1162
1162
|
returnValue,
|
|
1163
|
-
|
|
1163
|
+
resolve8,
|
|
1164
1164
|
reject
|
|
1165
1165
|
);
|
|
1166
1166
|
});
|
|
@@ -1174,7 +1174,7 @@ var require_react_development = __commonJS({
|
|
|
1174
1174
|
ReactSharedInternals.thrownErrors.length = 0;
|
|
1175
1175
|
reject(_thrownError);
|
|
1176
1176
|
}
|
|
1177
|
-
} else
|
|
1177
|
+
} else resolve8(returnValue);
|
|
1178
1178
|
},
|
|
1179
1179
|
function(error) {
|
|
1180
1180
|
popActScope(prevActQueue, prevActScopeDepth);
|
|
@@ -1196,15 +1196,15 @@ var require_react_development = __commonJS({
|
|
|
1196
1196
|
if (0 < ReactSharedInternals.thrownErrors.length)
|
|
1197
1197
|
throw callback = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, callback;
|
|
1198
1198
|
return {
|
|
1199
|
-
then: function(
|
|
1199
|
+
then: function(resolve8, reject) {
|
|
1200
1200
|
didAwaitActCall = true;
|
|
1201
1201
|
0 === prevActScopeDepth ? (ReactSharedInternals.actQueue = queue, enqueueTask(function() {
|
|
1202
1202
|
return recursivelyFlushAsyncActWork(
|
|
1203
1203
|
returnValue$jscomp$0,
|
|
1204
|
-
|
|
1204
|
+
resolve8,
|
|
1205
1205
|
reject
|
|
1206
1206
|
);
|
|
1207
|
-
})) :
|
|
1207
|
+
})) : resolve8(returnValue$jscomp$0);
|
|
1208
1208
|
}
|
|
1209
1209
|
};
|
|
1210
1210
|
};
|
|
@@ -12991,7 +12991,7 @@ A.finished.finally(function(){document.__reactViewTransition===A&&(document.__re
|
|
|
12991
12991
|
};
|
|
12992
12992
|
}
|
|
12993
12993
|
exports2.prerender = function(children, options) {
|
|
12994
|
-
return new Promise(function(
|
|
12994
|
+
return new Promise(function(resolve8, reject) {
|
|
12995
12995
|
var onHeaders = options ? options.onHeaders : void 0, onHeadersImpl;
|
|
12996
12996
|
onHeaders && (onHeadersImpl = function(headersDescriptor) {
|
|
12997
12997
|
onHeaders(new Headers(headersDescriptor));
|
|
@@ -13035,7 +13035,7 @@ A.finished.finally(function(){document.__reactViewTransition===A&&(document.__re
|
|
|
13035
13035
|
{ highWaterMark: 0 }
|
|
13036
13036
|
);
|
|
13037
13037
|
stream2 = { postponed: getPostponedState(request), prelude: stream2 };
|
|
13038
|
-
|
|
13038
|
+
resolve8(stream2);
|
|
13039
13039
|
},
|
|
13040
13040
|
void 0,
|
|
13041
13041
|
void 0,
|
|
@@ -13046,7 +13046,7 @@ A.finished.finally(function(){document.__reactViewTransition===A&&(document.__re
|
|
|
13046
13046
|
});
|
|
13047
13047
|
};
|
|
13048
13048
|
exports2.prerenderToNodeStream = function(children, options) {
|
|
13049
|
-
return new Promise(function(
|
|
13049
|
+
return new Promise(function(resolve8, reject) {
|
|
13050
13050
|
var resumableState = createResumableState(
|
|
13051
13051
|
options ? options.identifierPrefix : void 0,
|
|
13052
13052
|
options ? options.unstable_externalRuntimeSrc : void 0,
|
|
@@ -13078,7 +13078,7 @@ A.finished.finally(function(){document.__reactViewTransition===A&&(document.__re
|
|
|
13078
13078
|
postponed: getPostponedState(request),
|
|
13079
13079
|
prelude: readable
|
|
13080
13080
|
};
|
|
13081
|
-
|
|
13081
|
+
resolve8(readable);
|
|
13082
13082
|
},
|
|
13083
13083
|
void 0,
|
|
13084
13084
|
void 0,
|
|
@@ -13123,7 +13123,7 @@ A.finished.finally(function(){document.__reactViewTransition===A&&(document.__re
|
|
|
13123
13123
|
};
|
|
13124
13124
|
};
|
|
13125
13125
|
exports2.renderToReadableStream = function(children, options) {
|
|
13126
|
-
return new Promise(function(
|
|
13126
|
+
return new Promise(function(resolve8, reject) {
|
|
13127
13127
|
var onFatalError, onAllReady, allReady = new Promise(function(res, rej) {
|
|
13128
13128
|
onAllReady = res;
|
|
13129
13129
|
onFatalError = rej;
|
|
@@ -13173,7 +13173,7 @@ A.finished.finally(function(){document.__reactViewTransition===A&&(document.__re
|
|
|
13173
13173
|
{ highWaterMark: 0 }
|
|
13174
13174
|
);
|
|
13175
13175
|
stream2.allReady = allReady;
|
|
13176
|
-
|
|
13176
|
+
resolve8(stream2);
|
|
13177
13177
|
},
|
|
13178
13178
|
function(error) {
|
|
13179
13179
|
allReady.catch(function() {
|
|
@@ -13188,7 +13188,7 @@ A.finished.finally(function(){document.__reactViewTransition===A&&(document.__re
|
|
|
13188
13188
|
});
|
|
13189
13189
|
};
|
|
13190
13190
|
exports2.resume = function(children, postponedState, options) {
|
|
13191
|
-
return new Promise(function(
|
|
13191
|
+
return new Promise(function(resolve8, reject) {
|
|
13192
13192
|
var onFatalError, onAllReady, allReady = new Promise(function(res, rej) {
|
|
13193
13193
|
onAllReady = res;
|
|
13194
13194
|
onFatalError = rej;
|
|
@@ -13226,7 +13226,7 @@ A.finished.finally(function(){document.__reactViewTransition===A&&(document.__re
|
|
|
13226
13226
|
{ highWaterMark: 0 }
|
|
13227
13227
|
);
|
|
13228
13228
|
stream2.allReady = allReady;
|
|
13229
|
-
|
|
13229
|
+
resolve8(stream2);
|
|
13230
13230
|
},
|
|
13231
13231
|
function(error) {
|
|
13232
13232
|
allReady.catch(function() {
|
|
@@ -13240,7 +13240,7 @@ A.finished.finally(function(){document.__reactViewTransition===A&&(document.__re
|
|
|
13240
13240
|
});
|
|
13241
13241
|
};
|
|
13242
13242
|
exports2.resumeAndPrerender = function(children, postponedState, options) {
|
|
13243
|
-
return new Promise(function(
|
|
13243
|
+
return new Promise(function(resolve8, reject) {
|
|
13244
13244
|
var request = resumeAndPrerenderRequest(
|
|
13245
13245
|
children,
|
|
13246
13246
|
postponedState,
|
|
@@ -13272,7 +13272,7 @@ A.finished.finally(function(){document.__reactViewTransition===A&&(document.__re
|
|
|
13272
13272
|
{ highWaterMark: 0 }
|
|
13273
13273
|
);
|
|
13274
13274
|
stream2 = { postponed: getPostponedState(request), prelude: stream2 };
|
|
13275
|
-
|
|
13275
|
+
resolve8(stream2);
|
|
13276
13276
|
},
|
|
13277
13277
|
void 0,
|
|
13278
13278
|
void 0,
|
|
@@ -13283,7 +13283,7 @@ A.finished.finally(function(){document.__reactViewTransition===A&&(document.__re
|
|
|
13283
13283
|
});
|
|
13284
13284
|
};
|
|
13285
13285
|
exports2.resumeAndPrerenderToNodeStream = function(children, postponedState, options) {
|
|
13286
|
-
return new Promise(function(
|
|
13286
|
+
return new Promise(function(resolve8, reject) {
|
|
13287
13287
|
var request = resumeAndPrerenderRequest(
|
|
13288
13288
|
children,
|
|
13289
13289
|
postponedState,
|
|
@@ -13304,7 +13304,7 @@ A.finished.finally(function(){document.__reactViewTransition===A&&(document.__re
|
|
|
13304
13304
|
}
|
|
13305
13305
|
}), writable = createFakeWritableFromReadable(readable);
|
|
13306
13306
|
readable = { postponed: getPostponedState(request), prelude: readable };
|
|
13307
|
-
|
|
13307
|
+
resolve8(readable);
|
|
13308
13308
|
},
|
|
13309
13309
|
void 0,
|
|
13310
13310
|
void 0,
|
|
@@ -28776,7 +28776,7 @@ A.finished.finally(function(){document.__reactViewTransition===A&&(document.__re
|
|
|
28776
28776
|
ensureCorrectIsomorphicReactVersion();
|
|
28777
28777
|
ensureCorrectIsomorphicReactVersion();
|
|
28778
28778
|
exports2.prerender = function(children, options) {
|
|
28779
|
-
return new Promise(function(
|
|
28779
|
+
return new Promise(function(resolve8, reject) {
|
|
28780
28780
|
var onHeaders = options ? options.onHeaders : void 0, onHeadersImpl;
|
|
28781
28781
|
onHeaders && (onHeadersImpl = function(headersDescriptor) {
|
|
28782
28782
|
onHeaders(new Headers(headersDescriptor));
|
|
@@ -28825,7 +28825,7 @@ A.finished.finally(function(){document.__reactViewTransition===A&&(document.__re
|
|
|
28825
28825
|
postponed: getPostponedState(request),
|
|
28826
28826
|
prelude: stream2
|
|
28827
28827
|
};
|
|
28828
|
-
|
|
28828
|
+
resolve8(stream2);
|
|
28829
28829
|
},
|
|
28830
28830
|
void 0,
|
|
28831
28831
|
void 0,
|
|
@@ -28836,7 +28836,7 @@ A.finished.finally(function(){document.__reactViewTransition===A&&(document.__re
|
|
|
28836
28836
|
});
|
|
28837
28837
|
};
|
|
28838
28838
|
exports2.prerenderToNodeStream = function(children, options) {
|
|
28839
|
-
return new Promise(function(
|
|
28839
|
+
return new Promise(function(resolve8, reject) {
|
|
28840
28840
|
var resumableState = createResumableState(
|
|
28841
28841
|
options ? options.identifierPrefix : void 0,
|
|
28842
28842
|
options ? options.unstable_externalRuntimeSrc : void 0,
|
|
@@ -28868,7 +28868,7 @@ A.finished.finally(function(){document.__reactViewTransition===A&&(document.__re
|
|
|
28868
28868
|
postponed: getPostponedState(request),
|
|
28869
28869
|
prelude: readable
|
|
28870
28870
|
};
|
|
28871
|
-
|
|
28871
|
+
resolve8(readable);
|
|
28872
28872
|
},
|
|
28873
28873
|
void 0,
|
|
28874
28874
|
void 0,
|
|
@@ -28913,7 +28913,7 @@ A.finished.finally(function(){document.__reactViewTransition===A&&(document.__re
|
|
|
28913
28913
|
};
|
|
28914
28914
|
};
|
|
28915
28915
|
exports2.renderToReadableStream = function(children, options) {
|
|
28916
|
-
return new Promise(function(
|
|
28916
|
+
return new Promise(function(resolve8, reject) {
|
|
28917
28917
|
var onFatalError, onAllReady, allReady = new Promise(function(res, rej) {
|
|
28918
28918
|
onAllReady = res;
|
|
28919
28919
|
onFatalError = rej;
|
|
@@ -28963,7 +28963,7 @@ A.finished.finally(function(){document.__reactViewTransition===A&&(document.__re
|
|
|
28963
28963
|
{ highWaterMark: 0 }
|
|
28964
28964
|
);
|
|
28965
28965
|
stream2.allReady = allReady;
|
|
28966
|
-
|
|
28966
|
+
resolve8(stream2);
|
|
28967
28967
|
},
|
|
28968
28968
|
function(error) {
|
|
28969
28969
|
allReady.catch(function() {
|
|
@@ -28978,7 +28978,7 @@ A.finished.finally(function(){document.__reactViewTransition===A&&(document.__re
|
|
|
28978
28978
|
});
|
|
28979
28979
|
};
|
|
28980
28980
|
exports2.resume = function(children, postponedState, options) {
|
|
28981
|
-
return new Promise(function(
|
|
28981
|
+
return new Promise(function(resolve8, reject) {
|
|
28982
28982
|
var onFatalError, onAllReady, allReady = new Promise(function(res, rej) {
|
|
28983
28983
|
onAllReady = res;
|
|
28984
28984
|
onFatalError = rej;
|
|
@@ -29016,7 +29016,7 @@ A.finished.finally(function(){document.__reactViewTransition===A&&(document.__re
|
|
|
29016
29016
|
{ highWaterMark: 0 }
|
|
29017
29017
|
);
|
|
29018
29018
|
stream2.allReady = allReady;
|
|
29019
|
-
|
|
29019
|
+
resolve8(stream2);
|
|
29020
29020
|
},
|
|
29021
29021
|
function(error) {
|
|
29022
29022
|
allReady.catch(function() {
|
|
@@ -29030,7 +29030,7 @@ A.finished.finally(function(){document.__reactViewTransition===A&&(document.__re
|
|
|
29030
29030
|
});
|
|
29031
29031
|
};
|
|
29032
29032
|
exports2.resumeAndPrerender = function(children, postponedState, options) {
|
|
29033
|
-
return new Promise(function(
|
|
29033
|
+
return new Promise(function(resolve8, reject) {
|
|
29034
29034
|
var request = resumeAndPrerenderRequest(
|
|
29035
29035
|
children,
|
|
29036
29036
|
postponedState,
|
|
@@ -29064,7 +29064,7 @@ A.finished.finally(function(){document.__reactViewTransition===A&&(document.__re
|
|
|
29064
29064
|
{ highWaterMark: 0 }
|
|
29065
29065
|
);
|
|
29066
29066
|
stream2 = { postponed: getPostponedState(request), prelude: stream2 };
|
|
29067
|
-
|
|
29067
|
+
resolve8(stream2);
|
|
29068
29068
|
},
|
|
29069
29069
|
void 0,
|
|
29070
29070
|
void 0,
|
|
@@ -29075,7 +29075,7 @@ A.finished.finally(function(){document.__reactViewTransition===A&&(document.__re
|
|
|
29075
29075
|
});
|
|
29076
29076
|
};
|
|
29077
29077
|
exports2.resumeAndPrerenderToNodeStream = function(children, postponedState, options) {
|
|
29078
|
-
return new Promise(function(
|
|
29078
|
+
return new Promise(function(resolve8, reject) {
|
|
29079
29079
|
var request = resumeAndPrerenderRequest(
|
|
29080
29080
|
children,
|
|
29081
29081
|
postponedState,
|
|
@@ -29099,7 +29099,7 @@ A.finished.finally(function(){document.__reactViewTransition===A&&(document.__re
|
|
|
29099
29099
|
postponed: getPostponedState(request),
|
|
29100
29100
|
prelude: readable
|
|
29101
29101
|
};
|
|
29102
|
-
|
|
29102
|
+
resolve8(readable);
|
|
29103
29103
|
},
|
|
29104
29104
|
void 0,
|
|
29105
29105
|
void 0,
|
|
@@ -29482,9 +29482,9 @@ var require_jsx_runtime = __commonJS({
|
|
|
29482
29482
|
|
|
29483
29483
|
// src/cli.ts
|
|
29484
29484
|
var import_cross_spawn = __toESM(require("cross-spawn"));
|
|
29485
|
-
var
|
|
29486
|
-
var
|
|
29487
|
-
var
|
|
29485
|
+
var import_fs11 = require("fs");
|
|
29486
|
+
var import_os8 = require("os");
|
|
29487
|
+
var import_path11 = require("path");
|
|
29488
29488
|
|
|
29489
29489
|
// src/audit.ts
|
|
29490
29490
|
var import_fs = require("fs");
|
|
@@ -29603,12 +29603,6 @@ function validateConfig(config, label = CONFIG_FILE) {
|
|
|
29603
29603
|
return errors;
|
|
29604
29604
|
}
|
|
29605
29605
|
|
|
29606
|
-
// src/sources.ts
|
|
29607
|
-
var import_child_process2 = require("child_process");
|
|
29608
|
-
var import_fs5 = require("fs");
|
|
29609
|
-
var import_path5 = require("path");
|
|
29610
|
-
var import_dotenv3 = require("dotenv");
|
|
29611
|
-
|
|
29612
29606
|
// src/env.ts
|
|
29613
29607
|
var import_fs3 = require("fs");
|
|
29614
29608
|
var import_path3 = require("path");
|
|
@@ -29618,6 +29612,14 @@ function parseEnvFile(filePath) {
|
|
|
29618
29612
|
if (!(0, import_fs3.existsSync)(absPath)) return {};
|
|
29619
29613
|
return (0, import_dotenv.parse)((0, import_fs3.readFileSync)(absPath, "utf8"));
|
|
29620
29614
|
}
|
|
29615
|
+
function isDotenvxEncrypted(filePath) {
|
|
29616
|
+
try {
|
|
29617
|
+
const content = (0, import_fs3.readFileSync)((0, import_path3.resolve)(filePath), "utf8");
|
|
29618
|
+
return content.includes("DOTENV_PUBLIC_KEY") && /^[A-Za-z_][A-Za-z0-9_]*="?encrypted:/m.test(content);
|
|
29619
|
+
} catch {
|
|
29620
|
+
return false;
|
|
29621
|
+
}
|
|
29622
|
+
}
|
|
29621
29623
|
var PUBLIC_PREFIXES = ["NEXT_PUBLIC_", "VITE_", "EXPO_PUBLIC_"];
|
|
29622
29624
|
function extractPublicVars(environment = process.env) {
|
|
29623
29625
|
const vars = {};
|
|
@@ -29629,6 +29631,12 @@ function extractPublicVars(environment = process.env) {
|
|
|
29629
29631
|
return vars;
|
|
29630
29632
|
}
|
|
29631
29633
|
|
|
29634
|
+
// src/sources.ts
|
|
29635
|
+
var import_child_process2 = require("child_process");
|
|
29636
|
+
var import_fs5 = require("fs");
|
|
29637
|
+
var import_path5 = require("path");
|
|
29638
|
+
var import_dotenv3 = require("dotenv");
|
|
29639
|
+
|
|
29632
29640
|
// src/infisical.ts
|
|
29633
29641
|
var import_sdk = require("@infisical/sdk");
|
|
29634
29642
|
var import_child_process = require("child_process");
|
|
@@ -29739,7 +29747,7 @@ function assertSafeGlobalFile(file, context, entry) {
|
|
|
29739
29747
|
if ((mode & 63) !== 0) {
|
|
29740
29748
|
throw new SourceError(
|
|
29741
29749
|
entry.name,
|
|
29742
|
-
|
|
29750
|
+
entry.source.provider,
|
|
29743
29751
|
`[morphix-env] Global env file ${file} is readable by group or other users; set mode 0600 or pass --allow-insecure-global.`
|
|
29744
29752
|
);
|
|
29745
29753
|
}
|
|
@@ -29831,6 +29839,16 @@ function resolveDotenvx(entry, source, context) {
|
|
|
29831
29839
|
if (existingFiles.length !== files.length) {
|
|
29832
29840
|
throw new SourceError(entry.name, source.provider, `[morphix-env] dotenvx source "${entry.name}" is missing a declared env file.`);
|
|
29833
29841
|
}
|
|
29842
|
+
if (entry.globalImplicit && !context.allowInsecureGlobal && process.platform !== "win32") {
|
|
29843
|
+
const keysFile = (0, import_path5.join)((0, import_path5.dirname)(existingFiles[0]), ".env.keys");
|
|
29844
|
+
if ((0, import_fs5.existsSync)(keysFile) && ((0, import_fs5.statSync)(keysFile).mode & 63) !== 0) {
|
|
29845
|
+
throw new SourceError(
|
|
29846
|
+
entry.name,
|
|
29847
|
+
source.provider,
|
|
29848
|
+
`[morphix-env] Global dotenvx keys file ${keysFile} is readable by group or other users; set mode 0600 or pass --allow-insecure-global.`
|
|
29849
|
+
);
|
|
29850
|
+
}
|
|
29851
|
+
}
|
|
29834
29852
|
const command = ensureCommand("dotenvx", entry.name, source.provider);
|
|
29835
29853
|
const keys = [...new Set(existingFiles.flatMap((file) => Object.keys(parseEnvFile(file))))].filter((key) => !key.startsWith("DOTENV_PUBLIC_KEY"));
|
|
29836
29854
|
if (!keys.length) return { name: entry.name, provider: source.provider, values: {}, availability: "loaded" };
|
|
@@ -29838,8 +29856,10 @@ function resolveDotenvx(entry, source, context) {
|
|
|
29838
29856
|
let stage = "run";
|
|
29839
29857
|
try {
|
|
29840
29858
|
const output = (0, import_child_process2.execFileSync)(
|
|
29859
|
+
// --overload aligns dotenvx with every other provider: composed file
|
|
29860
|
+
// values win over same-named variables already in the parent env.
|
|
29841
29861
|
command,
|
|
29842
|
-
["run", "-f", existingFiles.join(","), "--", process.execPath, "-e", helper, JSON.stringify(keys)],
|
|
29862
|
+
["run", "--overload", "-f", existingFiles.join(","), "--", process.execPath, "-e", helper, JSON.stringify(keys)],
|
|
29843
29863
|
{
|
|
29844
29864
|
encoding: "utf8",
|
|
29845
29865
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -29862,7 +29882,8 @@ function resolveDotenvx(entry, source, context) {
|
|
|
29862
29882
|
return { name: entry.name, provider: source.provider, values, availability: "loaded" };
|
|
29863
29883
|
} catch (error) {
|
|
29864
29884
|
const category = typeof error?.code === "string" ? error.code : "failed";
|
|
29865
|
-
|
|
29885
|
+
const sniffHint = entry.globalImplicit ? " The global file looks dotenvx-encrypted; if it is actually plaintext, remove the DOTENV_PUBLIC_KEY / encrypted: markers." : "";
|
|
29886
|
+
throw new SourceError(entry.name, source.provider, `[morphix-env] dotenvx source "${entry.name}" could not decrypt or load (${stage}:${category}).${sniffHint}`);
|
|
29866
29887
|
}
|
|
29867
29888
|
}
|
|
29868
29889
|
var keytarModule;
|
|
@@ -30049,9 +30070,10 @@ function resolveEntries(layers, options) {
|
|
|
30049
30070
|
...includeGlobal ? Object.entries(layers.global.config.profiles || {}) : [],
|
|
30050
30071
|
...Object.entries(layers.project.config.profiles || {})
|
|
30051
30072
|
]);
|
|
30073
|
+
const globalEncrypted = isDotenvxEncrypted(layers.globalEnvFile);
|
|
30052
30074
|
const globalDotenv = includeGlobal ? [{
|
|
30053
30075
|
name: GLOBAL_DOTENV_SOURCE,
|
|
30054
|
-
source: { provider: "local", files: [layers.globalEnvFile], optional: true },
|
|
30076
|
+
source: globalEncrypted ? { provider: "dotenvx", files: [layers.globalEnvFile], optional: true } : { provider: "local", files: [layers.globalEnvFile], optional: true },
|
|
30055
30077
|
baseDir: layers.global.baseDir,
|
|
30056
30078
|
globalImplicit: true
|
|
30057
30079
|
}] : [];
|
|
@@ -37101,14 +37123,14 @@ async function startEditorServer(encodedState) {
|
|
|
37101
37123
|
});
|
|
37102
37124
|
const closeTimer = setTimeout(() => server.close(), EDITOR_LIFETIME_MS);
|
|
37103
37125
|
closeTimer.unref();
|
|
37104
|
-
await new Promise((
|
|
37126
|
+
await new Promise((resolve8, reject) => {
|
|
37105
37127
|
server.once("error", reject);
|
|
37106
37128
|
server.listen(0, "127.0.0.1", () => {
|
|
37107
37129
|
const address = server.address();
|
|
37108
37130
|
if (!address || typeof address === "string") return reject(new Error("Editor did not receive a loopback port"));
|
|
37109
37131
|
process.stdout.write(`http://127.0.0.1:${address.port}${editorPath(state)}
|
|
37110
37132
|
`);
|
|
37111
|
-
|
|
37133
|
+
resolve8();
|
|
37112
37134
|
});
|
|
37113
37135
|
});
|
|
37114
37136
|
}
|
|
@@ -37129,7 +37151,7 @@ async function launchEditor(entryScript, composition, selectedSource) {
|
|
|
37129
37151
|
detached: true,
|
|
37130
37152
|
stdio: ["ignore", "pipe", "ignore"]
|
|
37131
37153
|
});
|
|
37132
|
-
const url = await new Promise((
|
|
37154
|
+
const url = await new Promise((resolve8, reject) => {
|
|
37133
37155
|
const timer = setTimeout(() => reject(new Error("[morphix-env] Timed out starting local editor.")), 5e3);
|
|
37134
37156
|
child.once("error", () => {
|
|
37135
37157
|
clearTimeout(timer);
|
|
@@ -37139,7 +37161,7 @@ async function launchEditor(entryScript, composition, selectedSource) {
|
|
|
37139
37161
|
clearTimeout(timer);
|
|
37140
37162
|
const url2 = chunk.toString("utf8").trim();
|
|
37141
37163
|
child.stdout?.destroy();
|
|
37142
|
-
|
|
37164
|
+
resolve8(url2);
|
|
37143
37165
|
});
|
|
37144
37166
|
});
|
|
37145
37167
|
child.unref();
|
|
@@ -37154,6 +37176,267 @@ async function launchEditor(entryScript, composition, selectedSource) {
|
|
|
37154
37176
|
return { url, opened };
|
|
37155
37177
|
}
|
|
37156
37178
|
|
|
37179
|
+
// src/migrate.ts
|
|
37180
|
+
var import_child_process4 = require("child_process");
|
|
37181
|
+
var import_fs7 = require("fs");
|
|
37182
|
+
var import_os5 = require("os");
|
|
37183
|
+
var import_path7 = require("path");
|
|
37184
|
+
var import_dotenv4 = require("dotenv");
|
|
37185
|
+
function resolveUnitFile(rawFile, baseDir, homeDir) {
|
|
37186
|
+
const expanded = expandHomePath(rawFile, homeDir);
|
|
37187
|
+
return (0, import_path7.isAbsolute)(expanded) ? expanded : (0, import_path7.resolve)(baseDir, expanded);
|
|
37188
|
+
}
|
|
37189
|
+
function snapshotValues(unit) {
|
|
37190
|
+
const values = {};
|
|
37191
|
+
for (const file of unit.files) Object.assign(values, parseEnvFile(file));
|
|
37192
|
+
return values;
|
|
37193
|
+
}
|
|
37194
|
+
function declaredUnits(loaded, scope, homeDir) {
|
|
37195
|
+
const units = [];
|
|
37196
|
+
for (const [name, source] of Object.entries(loaded.config.sources || {})) {
|
|
37197
|
+
if (source.provider !== "local") continue;
|
|
37198
|
+
const files = source.files.map((file) => resolveUnitFile(file, loaded.baseDir, homeDir)).filter(import_fs7.existsSync);
|
|
37199
|
+
if (!files.length) continue;
|
|
37200
|
+
const probe = {
|
|
37201
|
+
name,
|
|
37202
|
+
kind: "declared",
|
|
37203
|
+
scope,
|
|
37204
|
+
baseDir: loaded.baseDir,
|
|
37205
|
+
configPath: loaded.path,
|
|
37206
|
+
source,
|
|
37207
|
+
files,
|
|
37208
|
+
keyNames: []
|
|
37209
|
+
};
|
|
37210
|
+
units.push({ ...probe, keyNames: Object.keys(snapshotValues(probe)).sort() });
|
|
37211
|
+
}
|
|
37212
|
+
return units;
|
|
37213
|
+
}
|
|
37214
|
+
function discoverPlaintextUnits(layers, homeDir = (0, import_os5.homedir)()) {
|
|
37215
|
+
const units = [];
|
|
37216
|
+
if ((0, import_fs7.existsSync)(layers.globalEnvFile) && !isDotenvxEncrypted(layers.globalEnvFile)) {
|
|
37217
|
+
const source = { provider: "local", files: [layers.globalEnvFile], optional: true };
|
|
37218
|
+
const probe = {
|
|
37219
|
+
name: GLOBAL_DOTENV_SOURCE,
|
|
37220
|
+
kind: "global-implicit",
|
|
37221
|
+
scope: null,
|
|
37222
|
+
baseDir: (0, import_path7.dirname)(layers.globalEnvFile),
|
|
37223
|
+
configPath: null,
|
|
37224
|
+
source,
|
|
37225
|
+
files: [layers.globalEnvFile],
|
|
37226
|
+
keyNames: []
|
|
37227
|
+
};
|
|
37228
|
+
units.push({ ...probe, keyNames: Object.keys(snapshotValues(probe)).sort() });
|
|
37229
|
+
}
|
|
37230
|
+
units.push(...declaredUnits(layers.global, "global", homeDir));
|
|
37231
|
+
units.push(...declaredUnits(layers.project, "project", homeDir));
|
|
37232
|
+
return units;
|
|
37233
|
+
}
|
|
37234
|
+
function legacyPlaintextHints(layers, units, homeDir = (0, import_os5.homedir)()) {
|
|
37235
|
+
const covered = new Set(units.flatMap((unit) => unit.files));
|
|
37236
|
+
const legacyFiles = layers.project.config.envFiles || [".env.local"];
|
|
37237
|
+
return legacyFiles.map((file) => resolveUnitFile(file, layers.project.baseDir, homeDir)).filter((file) => (0, import_fs7.existsSync)(file) && !covered.has(file) && !isDotenvxEncrypted(file));
|
|
37238
|
+
}
|
|
37239
|
+
function backupFile(file, stamp) {
|
|
37240
|
+
const backup = `${file}.mx-env-backup-${stamp}`;
|
|
37241
|
+
(0, import_fs7.copyFileSync)(file, backup);
|
|
37242
|
+
(0, import_fs7.chmodSync)(backup, 384);
|
|
37243
|
+
return backup;
|
|
37244
|
+
}
|
|
37245
|
+
var BACKUP_MARK = ".mx-env-backup-";
|
|
37246
|
+
function restoreBackups(backups) {
|
|
37247
|
+
const failed = [];
|
|
37248
|
+
for (const backup of backups) {
|
|
37249
|
+
const original = backup.slice(0, backup.lastIndexOf(BACKUP_MARK));
|
|
37250
|
+
try {
|
|
37251
|
+
if (original && (0, import_fs7.existsSync)(backup)) (0, import_fs7.copyFileSync)(backup, original);
|
|
37252
|
+
} catch {
|
|
37253
|
+
if (original) failed.push(original);
|
|
37254
|
+
}
|
|
37255
|
+
}
|
|
37256
|
+
return failed;
|
|
37257
|
+
}
|
|
37258
|
+
function withRestoreOutcome(message, failed, backups) {
|
|
37259
|
+
if (failed.length) {
|
|
37260
|
+
return `${message} RESTORE FAILED for: ${failed.join(", ")} \u2014 restore manually from the .mx-env-backup-* copies${backups.length ? ` (${backups.join(", ")})` : ""}.`;
|
|
37261
|
+
}
|
|
37262
|
+
return `${message} Plaintext and configuration were restored from backup${backups.length ? ` (${backups.join(", ")})` : ""}.`;
|
|
37263
|
+
}
|
|
37264
|
+
function equivalent(expected, actual) {
|
|
37265
|
+
const expectedKeys = Object.keys(expected).sort();
|
|
37266
|
+
const actualKeys = Object.keys(actual).sort();
|
|
37267
|
+
if (expectedKeys.length !== actualKeys.length) return false;
|
|
37268
|
+
return expectedKeys.every((key, index2) => key === actualKeys[index2] && expected[key] === actual[key]);
|
|
37269
|
+
}
|
|
37270
|
+
function existingDeclaredFiles(unit, homeDir) {
|
|
37271
|
+
const existing = new Set(unit.files);
|
|
37272
|
+
return unit.source.files.filter((file) => existing.has(resolveUnitFile(file, unit.baseDir, homeDir)));
|
|
37273
|
+
}
|
|
37274
|
+
function droppedDeclaredFiles(unit, homeDir) {
|
|
37275
|
+
const existing = new Set(unit.files);
|
|
37276
|
+
return unit.source.files.filter((file) => !existing.has(resolveUnitFile(file, unit.baseDir, homeDir)));
|
|
37277
|
+
}
|
|
37278
|
+
function rewriteDeclaredSource(unit, newSource, stamp, backups) {
|
|
37279
|
+
const configPath = unit.configPath;
|
|
37280
|
+
backups.push(backupFile(configPath, stamp));
|
|
37281
|
+
const parsed = JSON.parse((0, import_fs7.readFileSync)(configPath, "utf8"));
|
|
37282
|
+
parsed.sources[unit.name] = newSource;
|
|
37283
|
+
(0, import_fs7.writeFileSync)(configPath, `${JSON.stringify(parsed, null, 2)}
|
|
37284
|
+
`);
|
|
37285
|
+
}
|
|
37286
|
+
function gitIgnoreWarnings(paths) {
|
|
37287
|
+
const warnings = [];
|
|
37288
|
+
for (const path of paths) {
|
|
37289
|
+
try {
|
|
37290
|
+
(0, import_child_process4.execFileSync)("git", ["rev-parse", "--is-inside-work-tree"], { cwd: (0, import_path7.dirname)(path), stdio: ["ignore", "ignore", "ignore"] });
|
|
37291
|
+
} catch {
|
|
37292
|
+
continue;
|
|
37293
|
+
}
|
|
37294
|
+
try {
|
|
37295
|
+
(0, import_child_process4.execFileSync)("git", ["check-ignore", "-q", path], { cwd: (0, import_path7.dirname)(path), stdio: ["ignore", "ignore", "ignore"] });
|
|
37296
|
+
} catch (error) {
|
|
37297
|
+
if (error?.status === 1) {
|
|
37298
|
+
warnings.push(`[morphix-env] WARNING: ${path} is inside a git repository and NOT git-ignored \u2014 this file holds the decryption key and must never be committed.`);
|
|
37299
|
+
}
|
|
37300
|
+
}
|
|
37301
|
+
}
|
|
37302
|
+
return warnings;
|
|
37303
|
+
}
|
|
37304
|
+
async function reloadViaKeychain(unit, newSource, homeDir) {
|
|
37305
|
+
const entry = {
|
|
37306
|
+
name: unit.name,
|
|
37307
|
+
source: newSource,
|
|
37308
|
+
baseDir: unit.baseDir,
|
|
37309
|
+
globalImplicit: unit.kind === "global-implicit"
|
|
37310
|
+
};
|
|
37311
|
+
const loaded = await loadSource(entry, { homeDir, environment: { ...process.env } });
|
|
37312
|
+
return loaded.values;
|
|
37313
|
+
}
|
|
37314
|
+
function decryptValues(command, files) {
|
|
37315
|
+
const values = {};
|
|
37316
|
+
for (const file of files) {
|
|
37317
|
+
const output = (0, import_child_process4.execFileSync)(command, ["decrypt", "-f", file, "--stdout", "--no-native"], {
|
|
37318
|
+
encoding: "utf8",
|
|
37319
|
+
cwd: (0, import_path7.dirname)(file),
|
|
37320
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
37321
|
+
});
|
|
37322
|
+
const markerKeys = new Set(Object.keys(parseEnvFile(file)).filter((key) => key.startsWith("DOTENV_PUBLIC_KEY")));
|
|
37323
|
+
for (const [key, value] of Object.entries((0, import_dotenv4.parse)(output))) {
|
|
37324
|
+
if (!markerKeys.has(key)) values[key] = value;
|
|
37325
|
+
}
|
|
37326
|
+
}
|
|
37327
|
+
return values;
|
|
37328
|
+
}
|
|
37329
|
+
async function migrateToDotenvx(unit, stamp, homeDir, backups) {
|
|
37330
|
+
const snapshot = snapshotValues(unit);
|
|
37331
|
+
for (const file of unit.files) backups.push(backupFile(file, stamp));
|
|
37332
|
+
const candidateKeysFiles = [...new Set(unit.files.map((file) => (0, import_path7.join)((0, import_path7.dirname)(file), ".env.keys")))];
|
|
37333
|
+
const preExistingKeys = new Set(candidateKeysFiles.filter(import_fs7.existsSync));
|
|
37334
|
+
for (const keysFile of preExistingKeys) backups.push(backupFile(keysFile, stamp));
|
|
37335
|
+
try {
|
|
37336
|
+
let command;
|
|
37337
|
+
try {
|
|
37338
|
+
command = ensureCommand("dotenvx", unit.name, "dotenvx");
|
|
37339
|
+
} catch {
|
|
37340
|
+
throw new Error("the dotenvx CLI is required; install it (e.g. `npm i -g @dotenvx/dotenvx`) or migrate to another target.");
|
|
37341
|
+
}
|
|
37342
|
+
for (const file of unit.files) {
|
|
37343
|
+
(0, import_child_process4.execFileSync)(command, ["encrypt", "-f", file, "--no-native"], { cwd: (0, import_path7.dirname)(file), stdio: ["ignore", "ignore", "ignore"] });
|
|
37344
|
+
const keysFile = (0, import_path7.join)((0, import_path7.dirname)(file), ".env.keys");
|
|
37345
|
+
if ((0, import_fs7.existsSync)(keysFile)) (0, import_fs7.chmodSync)(keysFile, 384);
|
|
37346
|
+
}
|
|
37347
|
+
const keysFiles = candidateKeysFiles.filter(import_fs7.existsSync);
|
|
37348
|
+
const values = decryptValues(command, unit.files);
|
|
37349
|
+
if (!equivalent(snapshot, values)) {
|
|
37350
|
+
throw new Error("post-migration verification failed: decrypted values differ from the plaintext snapshot.");
|
|
37351
|
+
}
|
|
37352
|
+
const warnings = [];
|
|
37353
|
+
if (unit.kind === "declared") {
|
|
37354
|
+
const newSource = {
|
|
37355
|
+
provider: "dotenvx",
|
|
37356
|
+
files: existingDeclaredFiles(unit, homeDir),
|
|
37357
|
+
...unit.source.override ? { override: true } : {},
|
|
37358
|
+
...unit.source.optional ? { optional: true } : {}
|
|
37359
|
+
};
|
|
37360
|
+
rewriteDeclaredSource(unit, newSource, stamp, backups);
|
|
37361
|
+
for (const dropped of droppedDeclaredFiles(unit, homeDir)) {
|
|
37362
|
+
warnings.push(`[morphix-env] Warning: declared file "${dropped}" did not exist at migration time and was dropped from the rewritten source; re-add it to the config if it returns.`);
|
|
37363
|
+
}
|
|
37364
|
+
}
|
|
37365
|
+
return { warnings: [...warnings, ...gitIgnoreWarnings(keysFiles)] };
|
|
37366
|
+
} catch (error) {
|
|
37367
|
+
const failed = restoreBackups(backups);
|
|
37368
|
+
for (const keysFile of candidateKeysFiles) {
|
|
37369
|
+
if (!preExistingKeys.has(keysFile) && (0, import_fs7.existsSync)(keysFile)) (0, import_fs7.rmSync)(keysFile);
|
|
37370
|
+
}
|
|
37371
|
+
const message = error instanceof Error ? error.message : "unknown failure";
|
|
37372
|
+
throw new Error(withRestoreOutcome(message, failed, backups));
|
|
37373
|
+
}
|
|
37374
|
+
}
|
|
37375
|
+
function snapshotCovered(expected, actual) {
|
|
37376
|
+
return Object.keys(expected).every((key) => actual[key] === expected[key]);
|
|
37377
|
+
}
|
|
37378
|
+
async function migrateToKeychain(unit, service, stamp, homeDir, backups) {
|
|
37379
|
+
if (process.platform !== "darwin") throw new Error("the os-keychain target is supported on macOS only; use --to dotenvx on this machine.");
|
|
37380
|
+
const keytar = await loadKeytar();
|
|
37381
|
+
if (!keytar) throw new Error('the os-keychain target needs the optional native module "keytar"; reinstall morphix-env or use --to dotenvx.');
|
|
37382
|
+
const snapshot = snapshotValues(unit);
|
|
37383
|
+
for (const file of unit.files) backups.push(backupFile(file, stamp));
|
|
37384
|
+
const preExisting = new Map((await keytar.findCredentials(service)).map(({ account, password }) => [account, password]));
|
|
37385
|
+
try {
|
|
37386
|
+
for (const [key, value] of Object.entries(snapshot)) await keytar.setPassword(service, key, value);
|
|
37387
|
+
const newSource = {
|
|
37388
|
+
provider: "os-keychain",
|
|
37389
|
+
service,
|
|
37390
|
+
...unit.source.override ? { override: true } : {}
|
|
37391
|
+
};
|
|
37392
|
+
const values = await reloadViaKeychain(unit, newSource, homeDir);
|
|
37393
|
+
if (!snapshotCovered(snapshot, values)) {
|
|
37394
|
+
throw new Error("post-migration verification failed: keychain values differ from the plaintext snapshot.");
|
|
37395
|
+
}
|
|
37396
|
+
const foreign = Object.keys(values).filter((key) => !(key in snapshot));
|
|
37397
|
+
const warnings = foreign.length ? [`[morphix-env] Warning: keychain service "${service}" also holds ${foreign.length} unrelated account(s) (e.g. "${foreign[0]}"); verify the service is not shared unexpectedly.`] : [];
|
|
37398
|
+
rewriteDeclaredSource(unit, newSource, stamp, backups);
|
|
37399
|
+
for (const file of unit.files) (0, import_fs7.rmSync)(file);
|
|
37400
|
+
return { warnings };
|
|
37401
|
+
} catch (error) {
|
|
37402
|
+
const failed = restoreBackups(backups);
|
|
37403
|
+
for (const key of Object.keys(snapshot)) {
|
|
37404
|
+
if (preExisting.has(key)) {
|
|
37405
|
+
await keytar.setPassword(service, key, preExisting.get(key)).catch(() => {
|
|
37406
|
+
});
|
|
37407
|
+
} else {
|
|
37408
|
+
await keytar.deletePassword(service, key).catch(() => {
|
|
37409
|
+
});
|
|
37410
|
+
}
|
|
37411
|
+
}
|
|
37412
|
+
const message = error instanceof Error ? error.message : "unknown failure";
|
|
37413
|
+
throw new Error(withRestoreOutcome(message, failed, backups));
|
|
37414
|
+
}
|
|
37415
|
+
}
|
|
37416
|
+
async function executeUnit(unit, target, options = {}, homeDir = (0, import_os5.homedir)()) {
|
|
37417
|
+
if (unit.kind === "global-implicit" && target === "keychain") {
|
|
37418
|
+
return {
|
|
37419
|
+
unit,
|
|
37420
|
+
status: "skipped",
|
|
37421
|
+
backups: [],
|
|
37422
|
+
warnings: [],
|
|
37423
|
+
detail: "the global implicit file cannot migrate to os-keychain (its loading does not go through profiles); use --to dotenvx for it."
|
|
37424
|
+
};
|
|
37425
|
+
}
|
|
37426
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
37427
|
+
const backups = [];
|
|
37428
|
+
if (!unit.keyNames.length) {
|
|
37429
|
+
return { unit, status: "skipped", backups: [], warnings: [], detail: "no keys in the file(s); nothing to migrate." };
|
|
37430
|
+
}
|
|
37431
|
+
try {
|
|
37432
|
+
const { warnings } = target === "dotenvx" ? await migrateToDotenvx(unit, stamp, homeDir, backups) : await migrateToKeychain(unit, options.service || `mx-env:${unit.name}`, stamp, homeDir, backups);
|
|
37433
|
+
return { unit, status: "migrated", backups, warnings, detail: "" };
|
|
37434
|
+
} catch (error) {
|
|
37435
|
+
const message = error instanceof Error ? error.message : "unknown failure";
|
|
37436
|
+
return { unit, status: "failed", backups, warnings: [], detail: message };
|
|
37437
|
+
}
|
|
37438
|
+
}
|
|
37439
|
+
|
|
37157
37440
|
// src/redaction.ts
|
|
37158
37441
|
var StreamingRedactor = class {
|
|
37159
37442
|
constructor(values) {
|
|
@@ -37294,14 +37577,14 @@ var ReferenceResolver = class {
|
|
|
37294
37577
|
};
|
|
37295
37578
|
|
|
37296
37579
|
// src/skill.ts
|
|
37297
|
-
var
|
|
37298
|
-
var
|
|
37299
|
-
var
|
|
37580
|
+
var import_fs8 = require("fs");
|
|
37581
|
+
var import_os6 = require("os");
|
|
37582
|
+
var import_path8 = require("path");
|
|
37300
37583
|
var SKILL_TARGETS = ["global", "project", "trellis"];
|
|
37301
37584
|
function resolveSkillPath(target) {
|
|
37302
|
-
if (target === "project") return (0,
|
|
37303
|
-
if (target === "trellis") return (0,
|
|
37304
|
-
return (0,
|
|
37585
|
+
if (target === "project") return (0, import_path8.join)(process.cwd(), ".claude", "skills", "morphix-env", "SKILL.md");
|
|
37586
|
+
if (target === "trellis") return (0, import_path8.join)((0, import_os6.homedir)(), ".trellis", "skills", "morphix-env", "SKILL.md");
|
|
37587
|
+
return (0, import_path8.join)((0, import_os6.homedir)(), ".claude", "skills", "morphix-env", "SKILL.md");
|
|
37305
37588
|
}
|
|
37306
37589
|
function targetHint(target) {
|
|
37307
37590
|
if (target === "project") {
|
|
@@ -37320,17 +37603,17 @@ function printNextSteps() {
|
|
|
37320
37603
|
4. mx-env doc generate this project's key-to-source workflow Skill`);
|
|
37321
37604
|
}
|
|
37322
37605
|
function cmdSkill(subcommand, options) {
|
|
37323
|
-
const templatePath = (0,
|
|
37324
|
-
const template = (0,
|
|
37606
|
+
const templatePath = (0, import_path8.join)(__dirname, "SKILL.md");
|
|
37607
|
+
const template = (0, import_fs8.readFileSync)(templatePath, "utf8");
|
|
37325
37608
|
if (subcommand === "show") {
|
|
37326
37609
|
console.log(`[morphix-env] Skill template: ${templatePath}`);
|
|
37327
37610
|
for (const target2 of SKILL_TARGETS) {
|
|
37328
37611
|
const path = resolveSkillPath(target2);
|
|
37329
|
-
if (!(0,
|
|
37612
|
+
if (!(0, import_fs8.existsSync)(path)) {
|
|
37330
37613
|
console.log(` ${target2}: not installed (${path})`);
|
|
37331
37614
|
continue;
|
|
37332
37615
|
}
|
|
37333
|
-
const state = (0,
|
|
37616
|
+
const state = (0, import_fs8.readFileSync)(path, "utf8") === template ? "installed (matches this version)" : "installed (differs from this version)";
|
|
37334
37617
|
console.log(` ${target2}: ${state} (${path})`);
|
|
37335
37618
|
}
|
|
37336
37619
|
return 0;
|
|
@@ -37345,8 +37628,8 @@ function cmdSkill(subcommand, options) {
|
|
|
37345
37628
|
return 1;
|
|
37346
37629
|
}
|
|
37347
37630
|
const dest = resolveSkillPath(target);
|
|
37348
|
-
if ((0,
|
|
37349
|
-
if ((0,
|
|
37631
|
+
if ((0, import_fs8.existsSync)(dest)) {
|
|
37632
|
+
if ((0, import_fs8.readFileSync)(dest, "utf8") === template) {
|
|
37350
37633
|
console.log(`[morphix-env] Skill already installed and up to date: ${dest}`);
|
|
37351
37634
|
return 0;
|
|
37352
37635
|
}
|
|
@@ -37355,8 +37638,8 @@ function cmdSkill(subcommand, options) {
|
|
|
37355
37638
|
return 1;
|
|
37356
37639
|
}
|
|
37357
37640
|
}
|
|
37358
|
-
(0,
|
|
37359
|
-
(0,
|
|
37641
|
+
(0, import_fs8.mkdirSync)((0, import_path8.dirname)(dest), { recursive: true });
|
|
37642
|
+
(0, import_fs8.writeFileSync)(dest, template, { mode: 420 });
|
|
37360
37643
|
console.log(`[morphix-env] Installed skill: ${dest}`);
|
|
37361
37644
|
console.log(`[morphix-env] ${targetHint(target)}`);
|
|
37362
37645
|
printNextSteps();
|
|
@@ -37437,10 +37720,268 @@ async function startSecretServer(values) {
|
|
|
37437
37720
|
};
|
|
37438
37721
|
}
|
|
37439
37722
|
|
|
37723
|
+
// src/shims.ts
|
|
37724
|
+
var import_fs9 = require("fs");
|
|
37725
|
+
var import_os7 = require("os");
|
|
37726
|
+
var import_path9 = require("path");
|
|
37727
|
+
var SHIM_MARKER = "# mx-env shim \u2014 regenerated by `mx-env shell-init`; do not edit by hand.";
|
|
37728
|
+
var RESERVED_SHIM_NAMES = [
|
|
37729
|
+
"run",
|
|
37730
|
+
"fetch",
|
|
37731
|
+
"export",
|
|
37732
|
+
"render",
|
|
37733
|
+
"inject",
|
|
37734
|
+
"generate",
|
|
37735
|
+
"gen",
|
|
37736
|
+
"inspect",
|
|
37737
|
+
"migrate",
|
|
37738
|
+
"audit",
|
|
37739
|
+
"edit",
|
|
37740
|
+
"doc",
|
|
37741
|
+
"skill",
|
|
37742
|
+
"shell-init",
|
|
37743
|
+
"help",
|
|
37744
|
+
"mx-env",
|
|
37745
|
+
"morphix-env",
|
|
37746
|
+
"__mx_editor"
|
|
37747
|
+
];
|
|
37748
|
+
var SHIM_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
37749
|
+
function shimsDir(homeDir = (0, import_os7.homedir)()) {
|
|
37750
|
+
return (0, import_path9.join)(homeDir, ".mx-env", "shims");
|
|
37751
|
+
}
|
|
37752
|
+
function validateShimList(list, label) {
|
|
37753
|
+
if (list === void 0 || list === null) return [];
|
|
37754
|
+
if (!Array.isArray(list)) return [`${label}: "shims" must be an array of command names`];
|
|
37755
|
+
const errors = [];
|
|
37756
|
+
const seen = /* @__PURE__ */ new Set();
|
|
37757
|
+
for (const entry of list) {
|
|
37758
|
+
if (typeof entry !== "string" || !entry) {
|
|
37759
|
+
errors.push(`${label}: shim entries must be non-empty command names`);
|
|
37760
|
+
continue;
|
|
37761
|
+
}
|
|
37762
|
+
if (!SHIM_NAME_PATTERN.test(entry)) {
|
|
37763
|
+
errors.push(`${label}: shim entry "${entry}" must be a bare command name (letters, digits, ".", "_", "-"; no path separators or whitespace)`);
|
|
37764
|
+
continue;
|
|
37765
|
+
}
|
|
37766
|
+
const lower = entry.toLowerCase();
|
|
37767
|
+
if (RESERVED_SHIM_NAMES.includes(lower)) {
|
|
37768
|
+
errors.push(`${label}: shim entry "${entry}" conflicts with a reserved mx-env command name`);
|
|
37769
|
+
continue;
|
|
37770
|
+
}
|
|
37771
|
+
if (seen.has(lower)) {
|
|
37772
|
+
errors.push(`${label}: duplicate shim entry "${entry}" (comparison is case-insensitive)`);
|
|
37773
|
+
continue;
|
|
37774
|
+
}
|
|
37775
|
+
seen.add(lower);
|
|
37776
|
+
}
|
|
37777
|
+
return errors;
|
|
37778
|
+
}
|
|
37779
|
+
function resolveShimCommands(layers, includeGlobal = true) {
|
|
37780
|
+
const seen = /* @__PURE__ */ new Set();
|
|
37781
|
+
const commands = [];
|
|
37782
|
+
const push = (list) => {
|
|
37783
|
+
if (!Array.isArray(list)) return;
|
|
37784
|
+
for (const entry of list) {
|
|
37785
|
+
if (typeof entry !== "string") continue;
|
|
37786
|
+
const lower = entry.toLowerCase();
|
|
37787
|
+
if (seen.has(lower)) continue;
|
|
37788
|
+
seen.add(lower);
|
|
37789
|
+
commands.push(entry);
|
|
37790
|
+
}
|
|
37791
|
+
};
|
|
37792
|
+
if (includeGlobal) push(layers.global.config.shims);
|
|
37793
|
+
push(layers.project.config.shims);
|
|
37794
|
+
return commands;
|
|
37795
|
+
}
|
|
37796
|
+
function renderShimScript(command) {
|
|
37797
|
+
return `#!/bin/sh
|
|
37798
|
+
${SHIM_MARKER}
|
|
37799
|
+
command -v mx-env >/dev/null 2>&1 || { echo "[mx-env] mx-env not on PATH; reinstall morphix-env or run 'mx-env shell-init --remove' to clean up this shim." >&2; exit 127; }
|
|
37800
|
+
MXENV_SHIM=1 exec mx-env run -- ${command} "$@"
|
|
37801
|
+
`;
|
|
37802
|
+
}
|
|
37803
|
+
function readIfExists(path) {
|
|
37804
|
+
try {
|
|
37805
|
+
return (0, import_fs9.readFileSync)(path, "utf8");
|
|
37806
|
+
} catch {
|
|
37807
|
+
return null;
|
|
37808
|
+
}
|
|
37809
|
+
}
|
|
37810
|
+
function writeShims(commands, homeDir = (0, import_os7.homedir)()) {
|
|
37811
|
+
const dir = shimsDir(homeDir);
|
|
37812
|
+
(0, import_fs9.mkdirSync)(dir, { recursive: true, mode: 448 });
|
|
37813
|
+
const wanted = new Set(commands.map((command) => command.toLowerCase()));
|
|
37814
|
+
const written = [];
|
|
37815
|
+
const removed = [];
|
|
37816
|
+
const skipped = [];
|
|
37817
|
+
for (const file of (0, import_fs9.readdirSync)(dir)) {
|
|
37818
|
+
const path = (0, import_path9.join)(dir, file);
|
|
37819
|
+
const content = readIfExists(path);
|
|
37820
|
+
if (content === null || !content.includes(SHIM_MARKER)) continue;
|
|
37821
|
+
if (!wanted.has(file.toLowerCase())) {
|
|
37822
|
+
(0, import_fs9.rmSync)(path);
|
|
37823
|
+
removed.push(file);
|
|
37824
|
+
}
|
|
37825
|
+
}
|
|
37826
|
+
for (const command of commands) {
|
|
37827
|
+
const path = (0, import_path9.join)(dir, command);
|
|
37828
|
+
const content = renderShimScript(command);
|
|
37829
|
+
let isSymlink = false;
|
|
37830
|
+
try {
|
|
37831
|
+
isSymlink = (0, import_fs9.lstatSync)(path).isSymbolicLink();
|
|
37832
|
+
} catch {
|
|
37833
|
+
}
|
|
37834
|
+
const existing = isSymlink ? null : readIfExists(path);
|
|
37835
|
+
if (!isSymlink && existing !== null && !existing.includes(SHIM_MARKER)) {
|
|
37836
|
+
skipped.push(command);
|
|
37837
|
+
continue;
|
|
37838
|
+
}
|
|
37839
|
+
if (isSymlink) (0, import_fs9.rmSync)(path);
|
|
37840
|
+
else if (existing === content) continue;
|
|
37841
|
+
(0, import_fs9.writeFileSync)(path, content, { mode: 493 });
|
|
37842
|
+
(0, import_fs9.chmodSync)(path, 493);
|
|
37843
|
+
written.push(command);
|
|
37844
|
+
}
|
|
37845
|
+
return { written, removed, skipped };
|
|
37846
|
+
}
|
|
37847
|
+
function removeAllShims(homeDir = (0, import_os7.homedir)()) {
|
|
37848
|
+
const dir = shimsDir(homeDir);
|
|
37849
|
+
if (!(0, import_fs9.existsSync)(dir)) return [];
|
|
37850
|
+
const removed = [];
|
|
37851
|
+
for (const file of (0, import_fs9.readdirSync)(dir)) {
|
|
37852
|
+
const path = (0, import_path9.join)(dir, file);
|
|
37853
|
+
const content = readIfExists(path);
|
|
37854
|
+
if (content !== null && content.includes(SHIM_MARKER)) {
|
|
37855
|
+
(0, import_fs9.rmSync)(path);
|
|
37856
|
+
removed.push(file);
|
|
37857
|
+
}
|
|
37858
|
+
}
|
|
37859
|
+
return removed;
|
|
37860
|
+
}
|
|
37861
|
+
function detectHandmadeReservedShims(homeDir = (0, import_os7.homedir)()) {
|
|
37862
|
+
const dir = shimsDir(homeDir);
|
|
37863
|
+
if (!(0, import_fs9.existsSync)(dir)) return [];
|
|
37864
|
+
const found = [];
|
|
37865
|
+
for (const file of (0, import_fs9.readdirSync)(dir)) {
|
|
37866
|
+
if (!RESERVED_SHIM_NAMES.includes(file.toLowerCase())) continue;
|
|
37867
|
+
const content = readIfExists((0, import_path9.join)(dir, file));
|
|
37868
|
+
if (content === null || !content.includes(SHIM_MARKER)) found.push(file);
|
|
37869
|
+
}
|
|
37870
|
+
return found;
|
|
37871
|
+
}
|
|
37872
|
+
var RC_BLOCK_BEGIN = "# >>> mx-env shims >>>";
|
|
37873
|
+
var RC_BLOCK_END = "# <<< mx-env shims <<<";
|
|
37874
|
+
function rcBlock(homeDir = (0, import_os7.homedir)()) {
|
|
37875
|
+
const dir = shimsDir(homeDir);
|
|
37876
|
+
if (/["$`\\]/.test(dir)) {
|
|
37877
|
+
throw new Error("[morphix-env] Shims directory path contains characters that are unsafe in a shell rc line.");
|
|
37878
|
+
}
|
|
37879
|
+
return `${RC_BLOCK_BEGIN}
|
|
37880
|
+
export PATH="${dir}:$PATH"
|
|
37881
|
+
${RC_BLOCK_END}`;
|
|
37882
|
+
}
|
|
37883
|
+
function defaultRcPath(shell, homeDir = (0, import_os7.homedir)()) {
|
|
37884
|
+
const name = shell ? shell.split("/").pop() : "";
|
|
37885
|
+
if (name === "zsh") return (0, import_path9.join)(homeDir, ".zshrc");
|
|
37886
|
+
if (name === "bash") return (0, import_path9.join)(homeDir, ".bashrc");
|
|
37887
|
+
return null;
|
|
37888
|
+
}
|
|
37889
|
+
function writeFileAtomic(path, content) {
|
|
37890
|
+
const tmp = `${path}.mx-env-${process.pid}.tmp`;
|
|
37891
|
+
(0, import_fs9.writeFileSync)(tmp, content);
|
|
37892
|
+
(0, import_fs9.renameSync)(tmp, path);
|
|
37893
|
+
}
|
|
37894
|
+
function upsertRcBlock(rcPath, homeDir = (0, import_os7.homedir)()) {
|
|
37895
|
+
const existing = readIfExists(rcPath) ?? "";
|
|
37896
|
+
const block = rcBlock(homeDir);
|
|
37897
|
+
const pattern = new RegExp(`${escapeRegExp(RC_BLOCK_BEGIN)}[\\s\\S]*?${escapeRegExp(RC_BLOCK_END)}`, "g");
|
|
37898
|
+
if (pattern.test(existing)) {
|
|
37899
|
+
const replaced = existing.replace(pattern, block);
|
|
37900
|
+
if (replaced === existing) return "unchanged";
|
|
37901
|
+
writeFileAtomic(rcPath, replaced.endsWith("\n") ? replaced : `${replaced}
|
|
37902
|
+
`);
|
|
37903
|
+
return "updated";
|
|
37904
|
+
}
|
|
37905
|
+
const separator = existing.length && !existing.endsWith("\n") ? "\n" : "";
|
|
37906
|
+
writeFileAtomic(rcPath, `${existing}${separator}${block}
|
|
37907
|
+
`);
|
|
37908
|
+
return "added";
|
|
37909
|
+
}
|
|
37910
|
+
function removeRcBlock(rcPath) {
|
|
37911
|
+
const existing = readIfExists(rcPath);
|
|
37912
|
+
if (existing === null) return "absent";
|
|
37913
|
+
const pattern = new RegExp(`\\n?${escapeRegExp(RC_BLOCK_BEGIN)}[\\s\\S]*?${escapeRegExp(RC_BLOCK_END)}\\n?`, "g");
|
|
37914
|
+
if (!pattern.test(existing)) return "absent";
|
|
37915
|
+
writeFileAtomic(rcPath, existing.replace(pattern, "\n"));
|
|
37916
|
+
return "removed";
|
|
37917
|
+
}
|
|
37918
|
+
function escapeRegExp(text) {
|
|
37919
|
+
return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
37920
|
+
}
|
|
37921
|
+
function stripShimsFromPath(pathValue, homeDir = (0, import_os7.homedir)()) {
|
|
37922
|
+
if (!pathValue) return pathValue;
|
|
37923
|
+
const target = shimsDir(homeDir);
|
|
37924
|
+
let targetReal = target;
|
|
37925
|
+
try {
|
|
37926
|
+
targetReal = (0, import_fs9.realpathSync)(target);
|
|
37927
|
+
} catch {
|
|
37928
|
+
}
|
|
37929
|
+
const equal = (a, b) => process.platform === "darwin" ? a.toLowerCase() === b.toLowerCase() : a === b;
|
|
37930
|
+
const entries = pathValue.split(import_path9.delimiter).filter((entry) => {
|
|
37931
|
+
if (!entry) return true;
|
|
37932
|
+
const literal = entry.replace(/\/+$/, "");
|
|
37933
|
+
let resolved = literal;
|
|
37934
|
+
try {
|
|
37935
|
+
resolved = (0, import_fs9.realpathSync)(literal);
|
|
37936
|
+
} catch {
|
|
37937
|
+
}
|
|
37938
|
+
return !equal(literal, target) && !equal(resolved, targetReal);
|
|
37939
|
+
});
|
|
37940
|
+
return entries.join(import_path9.delimiter);
|
|
37941
|
+
}
|
|
37942
|
+
function sanitizeBootEnv(env = process.env, homeDir) {
|
|
37943
|
+
const originalPath = env.PATH;
|
|
37944
|
+
const viaShim = env.MXENV_SHIM === "1";
|
|
37945
|
+
try {
|
|
37946
|
+
const home = homeDir ?? (0, import_os7.homedir)();
|
|
37947
|
+
const stripped = stripShimsFromPath(originalPath, home);
|
|
37948
|
+
if (stripped !== originalPath) {
|
|
37949
|
+
if (stripped) env.PATH = stripped;
|
|
37950
|
+
else delete env.PATH;
|
|
37951
|
+
}
|
|
37952
|
+
} catch {
|
|
37953
|
+
}
|
|
37954
|
+
delete env.MXENV_SHIM;
|
|
37955
|
+
return { viaShim, originalPath };
|
|
37956
|
+
}
|
|
37957
|
+
function findShadowedCommands(commands, originalPath, homeDir = (0, import_os7.homedir)()) {
|
|
37958
|
+
if (!originalPath) return { absent: [...commands], shadowed: [] };
|
|
37959
|
+
const dirs = originalPath.split(import_path9.delimiter).filter(Boolean);
|
|
37960
|
+
const absent = [];
|
|
37961
|
+
const shadowed = [];
|
|
37962
|
+
for (const command of commands) {
|
|
37963
|
+
const shimPath = (0, import_path9.join)(shimsDir(homeDir), command);
|
|
37964
|
+
let resolvedTo = null;
|
|
37965
|
+
for (const dir of dirs) {
|
|
37966
|
+
const candidate = (0, import_path9.join)(dir, command);
|
|
37967
|
+
try {
|
|
37968
|
+
(0, import_fs9.accessSync)(candidate, import_fs9.constants.X_OK);
|
|
37969
|
+
resolvedTo = candidate;
|
|
37970
|
+
break;
|
|
37971
|
+
} catch {
|
|
37972
|
+
}
|
|
37973
|
+
}
|
|
37974
|
+
const samePath = (a, b) => process.platform === "darwin" ? a.toLowerCase() === b.toLowerCase() : a === b;
|
|
37975
|
+
if (resolvedTo === null) absent.push(command);
|
|
37976
|
+
else if (!samePath(resolvedTo, shimPath)) shadowed.push(command);
|
|
37977
|
+
}
|
|
37978
|
+
return { absent, shadowed };
|
|
37979
|
+
}
|
|
37980
|
+
|
|
37440
37981
|
// src/template.ts
|
|
37441
|
-
var
|
|
37442
|
-
var
|
|
37443
|
-
var
|
|
37982
|
+
var import_child_process5 = require("child_process");
|
|
37983
|
+
var import_fs10 = require("fs");
|
|
37984
|
+
var import_path10 = require("path");
|
|
37444
37985
|
function shellQuote(value) {
|
|
37445
37986
|
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
37446
37987
|
}
|
|
@@ -37487,20 +38028,20 @@ function renderTemplate(text, environment) {
|
|
|
37487
38028
|
return { output, missing: [...missing] };
|
|
37488
38029
|
}
|
|
37489
38030
|
function writePrivateFile(path, content) {
|
|
37490
|
-
const absolute = (0,
|
|
37491
|
-
(0,
|
|
37492
|
-
(0,
|
|
37493
|
-
(0,
|
|
38031
|
+
const absolute = (0, import_path10.resolve)(path);
|
|
38032
|
+
(0, import_fs10.mkdirSync)((0, import_path10.dirname)(absolute), { recursive: true, mode: 448 });
|
|
38033
|
+
(0, import_fs10.writeFileSync)(absolute, content, { mode: 384 });
|
|
38034
|
+
(0, import_fs10.chmodSync)(absolute, 384);
|
|
37494
38035
|
}
|
|
37495
38036
|
function gitOutputStatus(path) {
|
|
37496
|
-
const absolute = (0,
|
|
37497
|
-
const cwd = (0,
|
|
37498
|
-
const name = (0,
|
|
37499
|
-
const inside = (0,
|
|
38037
|
+
const absolute = (0, import_path10.resolve)(path);
|
|
38038
|
+
const cwd = (0, import_path10.dirname)(absolute);
|
|
38039
|
+
const name = (0, import_path10.basename)(absolute);
|
|
38040
|
+
const inside = (0, import_child_process5.spawnSync)("git", ["rev-parse", "--is-inside-work-tree"], { cwd, encoding: "utf8" });
|
|
37500
38041
|
if (inside.status !== 0) return "outside-repo";
|
|
37501
|
-
const tracked = (0,
|
|
38042
|
+
const tracked = (0, import_child_process5.spawnSync)("git", ["ls-files", "--error-unmatch", "--", name], { cwd, encoding: "utf8" });
|
|
37502
38043
|
if (tracked.status === 0) return "tracked";
|
|
37503
|
-
const ignored = (0,
|
|
38044
|
+
const ignored = (0, import_child_process5.spawnSync)("git", ["check-ignore", "-q", "--", name], { cwd, encoding: "utf8" });
|
|
37504
38045
|
if (ignored.status === 0) return "ignored";
|
|
37505
38046
|
if (ignored.status === 1) return "untracked";
|
|
37506
38047
|
return "outside-repo";
|
|
@@ -37517,7 +38058,7 @@ function warnOutputGitRisk(path) {
|
|
|
37517
38058
|
// package.json
|
|
37518
38059
|
var package_default = {
|
|
37519
38060
|
name: "morphix-env",
|
|
37520
|
-
version: "0.
|
|
38061
|
+
version: "0.10.0",
|
|
37521
38062
|
description: "Secret composition CLI \u2014 Infisical, Doppler, macOS Keychain, dotenvx, and local files stay value owners; composes them into one value-safe child environment and ships a built-in Agent Skill (mx-env skill install).",
|
|
37522
38063
|
author: "MorphixAI <dev@morphixai.com>",
|
|
37523
38064
|
homepage: "https://github.com/Morphicai/morphix-env#readme",
|
|
@@ -37622,9 +38163,15 @@ function parseArgs(argv) {
|
|
|
37622
38163
|
printEditorUrl: false,
|
|
37623
38164
|
source: null,
|
|
37624
38165
|
target: null,
|
|
38166
|
+
to: null,
|
|
38167
|
+
service: null,
|
|
37625
38168
|
force: false,
|
|
37626
38169
|
dryRun: false,
|
|
37627
|
-
serve: false
|
|
38170
|
+
serve: false,
|
|
38171
|
+
write: false,
|
|
38172
|
+
remove: false,
|
|
38173
|
+
warnPlaintext: false,
|
|
38174
|
+
unknownFlags: []
|
|
37628
38175
|
};
|
|
37629
38176
|
let i = 2;
|
|
37630
38177
|
const command = argv[i];
|
|
@@ -37661,24 +38208,36 @@ function parseArgs(argv) {
|
|
|
37661
38208
|
args.printEditorUrl = true;
|
|
37662
38209
|
} else if (arg === "--target") {
|
|
37663
38210
|
if (argv[++i]) args.target = argv[i];
|
|
38211
|
+
} else if (arg === "--to") {
|
|
38212
|
+
if (argv[++i]) args.to = argv[i];
|
|
38213
|
+
} else if (arg === "--service") {
|
|
38214
|
+
if (argv[++i]) args.service = argv[i];
|
|
37664
38215
|
} else if (arg === "--force") {
|
|
37665
38216
|
args.force = true;
|
|
37666
38217
|
} else if (arg === "--dry-run") {
|
|
37667
38218
|
args.dryRun = true;
|
|
38219
|
+
} else if (arg === "--write") {
|
|
38220
|
+
args.write = true;
|
|
38221
|
+
} else if (arg === "--remove") {
|
|
38222
|
+
args.remove = true;
|
|
37668
38223
|
} else if (arg === "--serve") {
|
|
37669
38224
|
args.serve = true;
|
|
38225
|
+
} else if (arg === "--warn-plaintext") {
|
|
38226
|
+
args.warnPlaintext = true;
|
|
37670
38227
|
} else if (args.command === "run") {
|
|
37671
38228
|
args.subArgs = argv.slice(i);
|
|
37672
38229
|
break;
|
|
37673
38230
|
} else if (!arg.startsWith("-")) {
|
|
37674
38231
|
args.positionals.push(arg);
|
|
38232
|
+
} else {
|
|
38233
|
+
args.unknownFlags.push(arg);
|
|
37675
38234
|
}
|
|
37676
38235
|
i++;
|
|
37677
38236
|
}
|
|
37678
38237
|
return args;
|
|
37679
38238
|
}
|
|
37680
38239
|
function configLayers() {
|
|
37681
|
-
return loadConfigLayers({ cwd: process.cwd(), homeDir: (0,
|
|
38240
|
+
return loadConfigLayers({ cwd: process.cwd(), homeDir: (0, import_os8.homedir)() });
|
|
37682
38241
|
}
|
|
37683
38242
|
function composeOptions(args) {
|
|
37684
38243
|
return {
|
|
@@ -37688,14 +38247,40 @@ function composeOptions(args) {
|
|
|
37688
38247
|
envName: args.env,
|
|
37689
38248
|
allowInsecureGlobal: args.allowInsecureGlobal,
|
|
37690
38249
|
envFiles: args.envFiles,
|
|
37691
|
-
homeDir: (0,
|
|
38250
|
+
homeDir: (0, import_os8.homedir)()
|
|
37692
38251
|
};
|
|
37693
38252
|
}
|
|
38253
|
+
var bootInfo = { viaShim: false };
|
|
38254
|
+
var shimWarned = false;
|
|
38255
|
+
function warnInvalidShims(layers, includeGlobal) {
|
|
38256
|
+
if (shimWarned) return;
|
|
38257
|
+
shimWarned = true;
|
|
38258
|
+
const problems = [
|
|
38259
|
+
...includeGlobal ? validateShimList(layers.global.config.shims, layers.global.path) : [],
|
|
38260
|
+
...validateShimList(layers.project.config.shims, layers.project.path)
|
|
38261
|
+
];
|
|
38262
|
+
if (problems.length) {
|
|
38263
|
+
console.warn(`[morphix-env] Ignoring invalid "shims" entries (${problems.length} problem(s)); run \`mx-env shell-init\` for the details.`);
|
|
38264
|
+
}
|
|
38265
|
+
}
|
|
37694
38266
|
function resolveComposition(args) {
|
|
37695
|
-
|
|
38267
|
+
const layers = configLayers();
|
|
38268
|
+
warnInvalidShims(layers, !args.noGlobal);
|
|
38269
|
+
return composeEnvironment(layers, composeOptions(args));
|
|
38270
|
+
}
|
|
38271
|
+
var plaintextWarned = false;
|
|
38272
|
+
function warnPlaintextSources(composition, args) {
|
|
38273
|
+
if (plaintextWarned) return;
|
|
38274
|
+
if (!args.warnPlaintext && process.env.MXENV_WARN_PLAINTEXT !== "1") return;
|
|
38275
|
+
plaintextWarned = true;
|
|
38276
|
+
const loaded = composition.sourceStatuses.filter(
|
|
38277
|
+
(source) => source.provider === "local" && source.availability === "loaded" && source.keyCount > 0
|
|
38278
|
+
);
|
|
38279
|
+
if (!loaded.length) return;
|
|
38280
|
+
console.warn(`[morphix-env] Plaintext local source(s) in use: ${loaded.map((source) => source.name).join(", ")} \u2014 local plaintext files are dev-only; run \`mx-env migrate\` to move them into encrypted storage.`);
|
|
37696
38281
|
}
|
|
37697
38282
|
function generatedOptions(args) {
|
|
37698
|
-
const config = loadConfigLayers({ cwd: process.cwd(), homeDir: (0,
|
|
38283
|
+
const config = loadConfigLayers({ cwd: process.cwd(), homeDir: (0, import_os8.homedir)() }).project.config;
|
|
37699
38284
|
return {
|
|
37700
38285
|
outFile: args.outFile || config.generate?.out || "public/__env.js",
|
|
37701
38286
|
filter: args.filter || config.generate?.filter || null
|
|
@@ -37704,12 +38289,15 @@ function generatedOptions(args) {
|
|
|
37704
38289
|
function generateClientEnv(outFile, filter, environment) {
|
|
37705
38290
|
let vars = extractPublicVars(environment);
|
|
37706
38291
|
if (filter) vars = Object.fromEntries(Object.entries(vars).filter(([key]) => key.startsWith(filter)));
|
|
37707
|
-
(0,
|
|
37708
|
-
(0,
|
|
38292
|
+
(0, import_fs11.mkdirSync)((0, import_path11.dirname)(outFile), { recursive: true });
|
|
38293
|
+
(0, import_fs11.writeFileSync)(outFile, `window.__ENV=${JSON.stringify(vars)};`);
|
|
37709
38294
|
console.log(`[morphix-env] Generated ${outFile} (${Object.keys(vars).length} client vars)`);
|
|
37710
38295
|
}
|
|
37711
38296
|
async function runChild(command, commandArgs, composition) {
|
|
37712
|
-
return new Promise((
|
|
38297
|
+
return new Promise((resolve8) => {
|
|
38298
|
+
if (composition.environment.PATH) {
|
|
38299
|
+
composition.environment.PATH = stripShimsFromPath(composition.environment.PATH) ?? composition.environment.PATH;
|
|
38300
|
+
}
|
|
37713
38301
|
const child = (0, import_cross_spawn.default)(command, commandArgs, { env: composition.environment, stdio: ["inherit", "pipe", "pipe"] });
|
|
37714
38302
|
const stdout = new StreamingRedactor(composition.redactionValues);
|
|
37715
38303
|
const stderr = new StreamingRedactor(composition.redactionValues);
|
|
@@ -37717,14 +38305,14 @@ async function runChild(command, commandArgs, composition) {
|
|
|
37717
38305
|
child.stderr?.on("data", (chunk) => process.stderr.write(stderr.write(chunk)));
|
|
37718
38306
|
child.on("error", () => {
|
|
37719
38307
|
console.error(`[morphix-env] Failed to execute: ${command}`);
|
|
37720
|
-
|
|
38308
|
+
resolve8(1);
|
|
37721
38309
|
});
|
|
37722
38310
|
child.on("close", (code) => {
|
|
37723
38311
|
const stdoutFinal = stdout.flush();
|
|
37724
38312
|
const stderrFinal = stderr.flush();
|
|
37725
38313
|
if (stdoutFinal) process.stdout.write(stdoutFinal);
|
|
37726
38314
|
if (stderrFinal) process.stderr.write(stderrFinal);
|
|
37727
|
-
|
|
38315
|
+
resolve8(code ?? 1);
|
|
37728
38316
|
});
|
|
37729
38317
|
});
|
|
37730
38318
|
}
|
|
@@ -37736,7 +38324,8 @@ function writeRunAudit(composition, startedAt, outcome, exitCode) {
|
|
|
37736
38324
|
keys: Object.keys(composition.provenance).sort(),
|
|
37737
38325
|
outcome,
|
|
37738
38326
|
exitCode,
|
|
37739
|
-
durationMs: Date.now() - startedAt
|
|
38327
|
+
durationMs: Date.now() - startedAt,
|
|
38328
|
+
via: bootInfo.viaShim ? "shim" : "cli"
|
|
37740
38329
|
});
|
|
37741
38330
|
}
|
|
37742
38331
|
async function startServeFor(composition, verbose) {
|
|
@@ -37789,6 +38378,7 @@ async function cmdRun(args) {
|
|
|
37789
38378
|
}
|
|
37790
38379
|
const startedAt = Date.now();
|
|
37791
38380
|
const composition = await resolveComposition(args);
|
|
38381
|
+
warnPlaintextSources(composition, args);
|
|
37792
38382
|
if (args.verbose) {
|
|
37793
38383
|
for (const source of composition.sourceStatuses) {
|
|
37794
38384
|
console.log(`[morphix-env] ${source.name}: ${source.availability} (${source.keyCount} keys)`);
|
|
@@ -37802,7 +38392,7 @@ async function cmdRun(args) {
|
|
|
37802
38392
|
await resolveEnvironmentReferences(composition, args);
|
|
37803
38393
|
if (args.outFile || generatedOptions(args).outFile) {
|
|
37804
38394
|
const { outFile, filter } = generatedOptions(args);
|
|
37805
|
-
const configured = Boolean(args.outFile || loadConfigLayers({ cwd: process.cwd(), homeDir: (0,
|
|
38395
|
+
const configured = Boolean(args.outFile || loadConfigLayers({ cwd: process.cwd(), homeDir: (0, import_os8.homedir)() }).project.config.generate);
|
|
37806
38396
|
if (configured) generateClientEnv(outFile, filter, composition.environment);
|
|
37807
38397
|
}
|
|
37808
38398
|
const [command, ...commandArgs] = args.subArgs;
|
|
@@ -37859,6 +38449,7 @@ async function cmdFetch(args) {
|
|
|
37859
38449
|
}
|
|
37860
38450
|
async function cmdGenerate(args) {
|
|
37861
38451
|
const composition = await resolveComposition(args);
|
|
38452
|
+
warnPlaintextSources(composition, args);
|
|
37862
38453
|
await resolveEnvironmentReferences(composition, args);
|
|
37863
38454
|
const { outFile, filter } = generatedOptions(args);
|
|
37864
38455
|
generateClientEnv(outFile, filter, composition.environment);
|
|
@@ -37871,7 +38462,8 @@ async function cmdInspect(args) {
|
|
|
37871
38462
|
const composition = await resolveComposition(args);
|
|
37872
38463
|
console.log(`profile: ${composition.profileName}`);
|
|
37873
38464
|
for (const source of composition.sourceStatuses) {
|
|
37874
|
-
|
|
38465
|
+
const plaintextMark = source.provider === "local" ? " [plaintext, dev-only]" : "";
|
|
38466
|
+
console.log(`source: ${source.name} (${source.provider}) \u2014 ${source.availability}, ${source.keyCount} keys${plaintextMark}`);
|
|
37875
38467
|
}
|
|
37876
38468
|
for (const [key, provenance] of Object.entries(composition.provenance).sort(([a], [b]) => a.localeCompare(b))) {
|
|
37877
38469
|
if (!args.filter || key.startsWith(args.filter)) {
|
|
@@ -37884,6 +38476,7 @@ async function cmdInspect(args) {
|
|
|
37884
38476
|
async function cmdExport(args) {
|
|
37885
38477
|
const startedAt = Date.now();
|
|
37886
38478
|
const composition = await resolveComposition(args);
|
|
38479
|
+
warnPlaintextSources(composition, args);
|
|
37887
38480
|
const keys = Object.keys(composition.provenance).sort();
|
|
37888
38481
|
if (args.dryRun) {
|
|
37889
38482
|
for (const key of keys) console.log(key);
|
|
@@ -37907,12 +38500,13 @@ async function cmdRender(args) {
|
|
|
37907
38500
|
console.error("[morphix-env] Usage: mx-env render <template-file> <output-file> [options]");
|
|
37908
38501
|
return 1;
|
|
37909
38502
|
}
|
|
37910
|
-
if (!(0,
|
|
38503
|
+
if (!(0, import_fs11.existsSync)(templatePath)) {
|
|
37911
38504
|
console.error(`[morphix-env] Template not found: ${templatePath}`);
|
|
37912
38505
|
return 1;
|
|
37913
38506
|
}
|
|
37914
|
-
const template = (0,
|
|
38507
|
+
const template = (0, import_fs11.readFileSync)(templatePath, "utf8");
|
|
37915
38508
|
const composition = await resolveComposition(args);
|
|
38509
|
+
warnPlaintextSources(composition, args);
|
|
37916
38510
|
const names = findTemplateVariables(template);
|
|
37917
38511
|
const missing = names.filter((name) => composition.environment[name] === void 0 || composition.environment[name] === null);
|
|
37918
38512
|
if (missing.length) console.warn(`[morphix-env] Missing variables: ${missing.join(", ")} (left unresolved)`);
|
|
@@ -37936,12 +38530,14 @@ async function cmdInject(args) {
|
|
|
37936
38530
|
console.error("[morphix-env] Usage: mx-env inject <template-file> <output-file> [options]");
|
|
37937
38531
|
return 1;
|
|
37938
38532
|
}
|
|
37939
|
-
if (!(0,
|
|
38533
|
+
if (!(0, import_fs11.existsSync)(templatePath)) {
|
|
37940
38534
|
console.error(`[morphix-env] Template not found: ${templatePath}`);
|
|
37941
38535
|
return 1;
|
|
37942
38536
|
}
|
|
37943
|
-
const template = (0,
|
|
37944
|
-
const
|
|
38537
|
+
const template = (0, import_fs11.readFileSync)(templatePath, "utf8");
|
|
38538
|
+
const layers = configLayers();
|
|
38539
|
+
warnInvalidShims(layers, !args.noGlobal);
|
|
38540
|
+
const resolver = new ReferenceResolver(layers, composeOptions(args));
|
|
37945
38541
|
const references = findMxEnvReferences(template);
|
|
37946
38542
|
if (args.dryRun) {
|
|
37947
38543
|
for (const reference of references) {
|
|
@@ -37974,7 +38570,7 @@ async function cmdEdit(args) {
|
|
|
37974
38570
|
}
|
|
37975
38571
|
const composition = await resolveComposition(args);
|
|
37976
38572
|
if (args.source && !composition.sourceEntries.some((entry) => entry.name === args.source)) {
|
|
37977
|
-
const layers = loadConfigLayers({ cwd: process.cwd(), homeDir: (0,
|
|
38573
|
+
const layers = loadConfigLayers({ cwd: process.cwd(), homeDir: (0, import_os8.homedir)() });
|
|
37978
38574
|
const declared = layers.project.config.sources?.[args.source] ? { source: layers.project.config.sources[args.source], baseDir: layers.project.baseDir } : !args.noGlobal && layers.global.config.sources?.[args.source] ? { source: layers.global.config.sources[args.source], baseDir: layers.global.baseDir } : null;
|
|
37979
38575
|
if (!declared) {
|
|
37980
38576
|
console.error(`[morphix-env] Unknown source "${args.source}".`);
|
|
@@ -37992,7 +38588,7 @@ async function cmdEdit(args) {
|
|
|
37992
38588
|
}
|
|
37993
38589
|
async function cmdDoc(args) {
|
|
37994
38590
|
const composition = await resolveComposition(args);
|
|
37995
|
-
const outFile = args.outFile || (0,
|
|
38591
|
+
const outFile = args.outFile || (0, import_path11.join)(process.cwd(), ".agents", "skills", "morphix-env", "SKILL.md");
|
|
37996
38592
|
const keyRows = Object.entries(composition.provenance).sort(([a], [b]) => a.localeCompare(b)).map(([key, provenance]) => `| \`${key}\` | ${provenance.source} (${provenance.provider}) |`).join("\n") || "| _No resolved keys_ | \u2014 |";
|
|
37997
38593
|
const skill = `---
|
|
37998
38594
|
name: morphix-env
|
|
@@ -38026,18 +38622,194 @@ ${keyRows}
|
|
|
38026
38622
|
|
|
38027
38623
|
Secret values and value prefixes do not belong in this Skill, command output, audit records, or conversation context.
|
|
38028
38624
|
`;
|
|
38029
|
-
(0,
|
|
38030
|
-
(0,
|
|
38625
|
+
(0, import_fs11.mkdirSync)((0, import_path11.dirname)(outFile), { recursive: true });
|
|
38626
|
+
(0, import_fs11.writeFileSync)(outFile, skill, { mode: 384 });
|
|
38031
38627
|
console.log(`[morphix-env] Generated value-free workflow Skill: ${outFile}`);
|
|
38032
38628
|
return 0;
|
|
38033
38629
|
}
|
|
38630
|
+
function cmdShellInit(args) {
|
|
38631
|
+
if (process.platform === "win32") {
|
|
38632
|
+
console.error("[morphix-env] shell-init is supported on macOS/Linux only; on Windows keep using explicit `mx-env run -- <command>`.");
|
|
38633
|
+
return 1;
|
|
38634
|
+
}
|
|
38635
|
+
if (args.unknownFlags.length) {
|
|
38636
|
+
console.error(`[morphix-env] shell-init: unrecognized flag(s): ${args.unknownFlags.join(", ")}`);
|
|
38637
|
+
return 1;
|
|
38638
|
+
}
|
|
38639
|
+
if (args.write && args.remove) {
|
|
38640
|
+
console.error("[morphix-env] shell-init: --write and --remove are mutually exclusive.");
|
|
38641
|
+
return 1;
|
|
38642
|
+
}
|
|
38643
|
+
try {
|
|
38644
|
+
return runShellInit(args);
|
|
38645
|
+
} catch (error) {
|
|
38646
|
+
const code = error?.code;
|
|
38647
|
+
console.error(`[morphix-env] shell-init failed${code ? ` (${code})` : ""}: check that your home directory and rc file are writable, then retry.`);
|
|
38648
|
+
return 1;
|
|
38649
|
+
}
|
|
38650
|
+
}
|
|
38651
|
+
function runShellInit(args) {
|
|
38652
|
+
const layers = configLayers();
|
|
38653
|
+
const home = (0, import_os8.homedir)();
|
|
38654
|
+
const rcPath = defaultRcPath(process.env.SHELL, home);
|
|
38655
|
+
if (args.remove) {
|
|
38656
|
+
const removed2 = removeAllShims(home);
|
|
38657
|
+
const rcOutcome = rcPath ? removeRcBlock(rcPath) : "absent";
|
|
38658
|
+
console.log(`[morphix-env] Removed ${removed2.length} shim(s)${removed2.length ? ` (${removed2.join(", ")})` : ""}; rc block ${rcOutcome}${rcPath ? ` in ${rcPath}` : ""}.`);
|
|
38659
|
+
return 0;
|
|
38660
|
+
}
|
|
38661
|
+
const problems = [
|
|
38662
|
+
...validateShimList(layers.global.config.shims, layers.global.path),
|
|
38663
|
+
...validateShimList(layers.project.config.shims, layers.project.path)
|
|
38664
|
+
];
|
|
38665
|
+
if (problems.length) {
|
|
38666
|
+
for (const problem of problems) console.error(`[morphix-env] ${problem}`);
|
|
38667
|
+
return 1;
|
|
38668
|
+
}
|
|
38669
|
+
const commands = resolveShimCommands(layers, !args.noGlobal);
|
|
38670
|
+
const { written, removed, skipped } = writeShims(commands, home);
|
|
38671
|
+
console.log(`[morphix-env] Shims ready in ${shimsDir(home)} (${commands.length} command(s): ${commands.join(", ") || "none"}; ${written.length} written, ${removed.length} removed).`);
|
|
38672
|
+
for (const command of skipped) {
|
|
38673
|
+
console.warn(`[morphix-env] Warning: "${command}" exists in the shims directory as a handmade file (no mx-env marker); refusing to overwrite. Remove or rename it by hand, then re-run shell-init.`);
|
|
38674
|
+
}
|
|
38675
|
+
for (const file of detectHandmadeReservedShims(home)) {
|
|
38676
|
+
console.warn(`[morphix-env] Warning: "${file}" in the shims directory was not created by mx-env and shadows a reserved command; delete it by hand. Do not place files in ${shimsDir(home)} manually.`);
|
|
38677
|
+
}
|
|
38678
|
+
const { absent, shadowed } = findShadowedCommands(commands, bootInfo.originalPath, home);
|
|
38679
|
+
if (absent.length) {
|
|
38680
|
+
console.warn(`[morphix-env] Note: ${absent.join(", ")} cannot be resolved on the current PATH yet \u2014 add the shims directory (below) and open a new shell.`);
|
|
38681
|
+
}
|
|
38682
|
+
if (shadowed.length) {
|
|
38683
|
+
console.warn(`[morphix-env] Warning: these commands resolve to a different executable ahead of their shim: ${shadowed.join(", ")} \u2014 move ${shimsDir(home)} ahead of the entries that provide them.`);
|
|
38684
|
+
}
|
|
38685
|
+
if (args.write) {
|
|
38686
|
+
if (!rcPath) {
|
|
38687
|
+
console.error("[morphix-env] shell-init: cannot infer an rc file from $SHELL (zsh/bash supported); add the PATH line manually.");
|
|
38688
|
+
return 1;
|
|
38689
|
+
}
|
|
38690
|
+
const outcome = upsertRcBlock(rcPath, home);
|
|
38691
|
+
console.log(`[morphix-env] rc block ${outcome} in ${rcPath}. Open a new shell to activate.`);
|
|
38692
|
+
return 0;
|
|
38693
|
+
}
|
|
38694
|
+
console.log(`
|
|
38695
|
+
Add the shims directory to PATH${rcPath ? ` (e.g. in ${rcPath})` : ""}:
|
|
38696
|
+
|
|
38697
|
+
export PATH="${shimsDir(home)}:$PATH"
|
|
38698
|
+
|
|
38699
|
+
Or re-run with --write to let mx-env maintain a marked block in your rc file.`);
|
|
38700
|
+
return 0;
|
|
38701
|
+
}
|
|
38702
|
+
function printMigratePlan(units, hints, target) {
|
|
38703
|
+
if (!units.length) {
|
|
38704
|
+
console.log("[morphix-env] No plaintext units in scope; nothing to migrate.");
|
|
38705
|
+
}
|
|
38706
|
+
for (const unit of units) {
|
|
38707
|
+
const where = unit.kind === "global-implicit" ? "global implicit" : `${unit.scope}, declared in ${unit.configPath}`;
|
|
38708
|
+
console.log(`unit: ${unit.name} (${where})`);
|
|
38709
|
+
for (const file of unit.files) console.log(` file: ${file}`);
|
|
38710
|
+
console.log(` keys: ${unit.keyNames.join(", ") || "(none)"}`);
|
|
38711
|
+
console.log(` --to dotenvx: encrypt file(s) in place${unit.kind === "declared" ? " + rewrite provider in config" : " (no config change; loading auto-detects encryption)"}`);
|
|
38712
|
+
console.log(unit.kind === "global-implicit" ? " --to keychain: not supported for the global implicit file" : " --to keychain: move values to a keychain service, rewrite config, delete plaintext");
|
|
38713
|
+
}
|
|
38714
|
+
for (const hint of hints) {
|
|
38715
|
+
console.log(`legacy plaintext (not auto-migrated): ${hint} \u2014 declare it as a source in mx-env.config.json to migrate it.`);
|
|
38716
|
+
}
|
|
38717
|
+
if (units.length && !target) {
|
|
38718
|
+
console.log("\nPlan only. Re-run with --to dotenvx|keychain to execute; every unit gets a 0600 backup and a post-migration equivalence check before its config changes.");
|
|
38719
|
+
}
|
|
38720
|
+
}
|
|
38721
|
+
async function cmdMigrate(args) {
|
|
38722
|
+
if (args.unknownFlags.length) {
|
|
38723
|
+
console.error(`[morphix-env] migrate: unrecognized flag(s): ${args.unknownFlags.join(", ")}`);
|
|
38724
|
+
return 1;
|
|
38725
|
+
}
|
|
38726
|
+
if (args.to && args.to !== "dotenvx" && args.to !== "keychain") {
|
|
38727
|
+
console.error('[morphix-env] migrate: --to must be "dotenvx" or "keychain".');
|
|
38728
|
+
return 1;
|
|
38729
|
+
}
|
|
38730
|
+
if (args.service && args.to !== "keychain") {
|
|
38731
|
+
console.error("[morphix-env] migrate: --service is only valid with --to keychain.");
|
|
38732
|
+
return 1;
|
|
38733
|
+
}
|
|
38734
|
+
if (args.service && !args.source) {
|
|
38735
|
+
console.error("[morphix-env] migrate: --service requires --source (one service per migrated source).");
|
|
38736
|
+
return 1;
|
|
38737
|
+
}
|
|
38738
|
+
const layers = configLayers();
|
|
38739
|
+
const home = (0, import_os8.homedir)();
|
|
38740
|
+
const units = discoverPlaintextUnits(layers, home);
|
|
38741
|
+
const hints = legacyPlaintextHints(layers, units, home);
|
|
38742
|
+
const fileOwners = /* @__PURE__ */ new Map();
|
|
38743
|
+
const collisions = [];
|
|
38744
|
+
for (const unit of units) {
|
|
38745
|
+
for (const file of unit.files) {
|
|
38746
|
+
const owner = fileOwners.get(file);
|
|
38747
|
+
if (owner && owner !== unit.name) {
|
|
38748
|
+
collisions.push(`file ${file} is shared by units "${owner}" and "${unit.name}"`);
|
|
38749
|
+
}
|
|
38750
|
+
if (!owner) fileOwners.set(file, unit.name);
|
|
38751
|
+
}
|
|
38752
|
+
}
|
|
38753
|
+
for (const collision of collisions) {
|
|
38754
|
+
if (!args.to || args.dryRun) console.warn(`[morphix-env] Warning: ${collision}; split them into separate files (or sources) before migrating.`);
|
|
38755
|
+
else {
|
|
38756
|
+
console.error(`[morphix-env] migrate: ${collision}; split them into separate files (or sources) before migrating.`);
|
|
38757
|
+
return 1;
|
|
38758
|
+
}
|
|
38759
|
+
}
|
|
38760
|
+
const selected = args.source ? units.filter((unit) => unit.name === args.source) : units;
|
|
38761
|
+
if (args.source && !selected.length) {
|
|
38762
|
+
console.error(`[morphix-env] migrate: no plaintext unit named "${args.source}" in scope (already migrated or unknown).`);
|
|
38763
|
+
return 1;
|
|
38764
|
+
}
|
|
38765
|
+
if (!args.to || args.dryRun) {
|
|
38766
|
+
printMigratePlan(selected, hints, args.to);
|
|
38767
|
+
return 0;
|
|
38768
|
+
}
|
|
38769
|
+
if (!selected.length) {
|
|
38770
|
+
console.log("[morphix-env] No plaintext units in scope; nothing to migrate.");
|
|
38771
|
+
return 0;
|
|
38772
|
+
}
|
|
38773
|
+
const target = args.to;
|
|
38774
|
+
const startedAt = Date.now();
|
|
38775
|
+
const outcomes = [];
|
|
38776
|
+
for (const unit of selected) {
|
|
38777
|
+
const outcome = await executeUnit(unit, target, { service: args.service || void 0 }, home);
|
|
38778
|
+
outcomes.push(outcome);
|
|
38779
|
+
if (outcome.status === "migrated") {
|
|
38780
|
+
console.log(`[morphix-env] Migrated ${unit.name} -> ${target} (${unit.keyNames.length} keys).`);
|
|
38781
|
+
for (const warning of outcome.warnings) console.warn(warning);
|
|
38782
|
+
for (const backup of outcome.backups) console.log(`[morphix-env] Backup (0600): ${backup}`);
|
|
38783
|
+
} else if (outcome.status === "skipped") {
|
|
38784
|
+
console.warn(`[morphix-env] Skipped ${unit.name}: ${outcome.detail}`);
|
|
38785
|
+
} else {
|
|
38786
|
+
console.error(`[morphix-env] Failed ${unit.name}: ${outcome.detail}`);
|
|
38787
|
+
for (const backup of outcome.backups) console.error(`[morphix-env] Backup retained (0600): ${backup} \u2014 delete it by hand once verified.`);
|
|
38788
|
+
}
|
|
38789
|
+
}
|
|
38790
|
+
const failed = outcomes.filter((outcome) => outcome.status === "failed");
|
|
38791
|
+
const exitCode = failed.length ? 1 : 0;
|
|
38792
|
+
writeAudit({
|
|
38793
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
38794
|
+
profile: "migrate",
|
|
38795
|
+
sources: selected.map((unit) => unit.name),
|
|
38796
|
+
keys: [...new Set(selected.flatMap((unit) => unit.keyNames))].sort(),
|
|
38797
|
+
outcome: failed.length ? "failure" : "success",
|
|
38798
|
+
exitCode,
|
|
38799
|
+
durationMs: Date.now() - startedAt
|
|
38800
|
+
});
|
|
38801
|
+
if (outcomes.some((outcome) => outcome.backups.length)) {
|
|
38802
|
+
console.log("[morphix-env] Backups are 0600 copies of the original plaintext \u2014 delete them once your workflows are verified.");
|
|
38803
|
+
}
|
|
38804
|
+
return exitCode;
|
|
38805
|
+
}
|
|
38034
38806
|
function cmdAudit(args) {
|
|
38035
|
-
const file = (0,
|
|
38036
|
-
if (!(0,
|
|
38807
|
+
const file = (0, import_path11.join)((0, import_os8.homedir)(), ".mx-env", "audit", `${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}.jsonl`);
|
|
38808
|
+
if (!(0, import_fs11.existsSync)(file)) {
|
|
38037
38809
|
console.log("[morphix-env] No audit events for today.");
|
|
38038
38810
|
return 0;
|
|
38039
38811
|
}
|
|
38040
|
-
const lines = (0,
|
|
38812
|
+
const lines = (0, import_fs11.readFileSync)(file, "utf8").trim().split("\n").filter(Boolean);
|
|
38041
38813
|
const selected = args.subArgs[0] === "tail" ? lines.slice(-20) : lines;
|
|
38042
38814
|
for (const line of selected) console.log(line);
|
|
38043
38815
|
return 0;
|
|
@@ -38054,11 +38826,13 @@ Usage:
|
|
|
38054
38826
|
mx-env inject <template> <output> Resolve mx-env:// references into a file (mode 0600)
|
|
38055
38827
|
mx-env generate [options] Generate public __env.js
|
|
38056
38828
|
mx-env inspect [options] Print key names, sources, and status only
|
|
38829
|
+
mx-env migrate [--to dotenvx|keychain] Move plaintext local sources into encrypted storage
|
|
38057
38830
|
mx-env doc [options] Generate a value-free project Skill
|
|
38058
38831
|
mx-env audit [tail] Read value-free local audit events
|
|
38059
38832
|
mx-env edit --profile <name> Open the one-shot visual editor
|
|
38060
38833
|
mx-env skill install [--target <t>] Install the Agent Skill (global|project|trellis)
|
|
38061
38834
|
mx-env skill show Show skill install status per target
|
|
38835
|
+
mx-env shell-init [--write|--remove] Generate PATH shims for declared commands
|
|
38062
38836
|
|
|
38063
38837
|
Both mx-env and morphix-env are supported.
|
|
38064
38838
|
|
|
@@ -38073,8 +38847,12 @@ Options:
|
|
|
38073
38847
|
--no-infisical Do not load Infisical sources
|
|
38074
38848
|
--no-global Ignore ~/.mx-env/.env and ~/.mx-env/config.json
|
|
38075
38849
|
--allow-insecure-global Allow a global .env with permissive file mode
|
|
38850
|
+
--warn-plaintext Warn once on stderr when plaintext local sources supply keys
|
|
38076
38851
|
--print-editor-url Explicitly print the one-time local editor URL
|
|
38077
38852
|
--target <scope> Skill install target: global, project, or trellis
|
|
38853
|
+
--to <provider> migrate destination: dotenvx or keychain (omit for a read-only plan)
|
|
38854
|
+
--source <name> edit/migrate: select a single source
|
|
38855
|
+
--service <name> migrate --to keychain: keychain service override (requires --source)
|
|
38078
38856
|
--force Overwrite an installed skill that differs from this version
|
|
38079
38857
|
-v, --verbose Print source status and key counts only
|
|
38080
38858
|
--help, -h Show this help
|
|
@@ -38082,6 +38860,7 @@ Options:
|
|
|
38082
38860
|
`);
|
|
38083
38861
|
}
|
|
38084
38862
|
async function main() {
|
|
38863
|
+
Object.assign(bootInfo, sanitizeBootEnv());
|
|
38085
38864
|
const args = parseArgs(process.argv);
|
|
38086
38865
|
switch (args.command) {
|
|
38087
38866
|
case "run":
|
|
@@ -38099,12 +38878,16 @@ async function main() {
|
|
|
38099
38878
|
return cmdGenerate(args);
|
|
38100
38879
|
case "inspect":
|
|
38101
38880
|
return cmdInspect(args);
|
|
38881
|
+
case "migrate":
|
|
38882
|
+
return cmdMigrate(args);
|
|
38102
38883
|
case "audit":
|
|
38103
38884
|
return cmdAudit(args);
|
|
38104
38885
|
case "edit":
|
|
38105
38886
|
return cmdEdit(args);
|
|
38106
38887
|
case "doc":
|
|
38107
38888
|
return cmdDoc(args);
|
|
38889
|
+
case "shell-init":
|
|
38890
|
+
return cmdShellInit(args);
|
|
38108
38891
|
case "skill": {
|
|
38109
38892
|
const positional = process.argv.slice(3).find((arg) => !arg.startsWith("-")) ?? "install";
|
|
38110
38893
|
return cmdSkill(positional, { target: args.target, force: args.force });
|