@jskit-ai/jskit-cli 0.2.126 → 0.2.127
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +4 -4
- package/src/server/cliRuntime/mutations/textMutations.js +22 -4
- package/src/server/cliRuntime/packageInstallFlow.js +6 -7
- package/src/server/cliRuntime/sensitiveLockState.js +234 -0
- package/src/server/commandHandlers/packageCommands/add.js +44 -5
- package/src/server/commandHandlers/packageCommands/create.js +4 -0
- package/src/server/commandHandlers/packageCommands/migrations.js +20 -1
- package/src/server/commandHandlers/packageCommands/position.js +22 -2
- package/src/server/commandHandlers/packageCommands/remove.js +14 -0
- package/src/server/core/commandCatalog.js +2 -1
- package/src/server/shared/optionInterpolation.js +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jskit-ai/jskit-cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.127",
|
|
4
4
|
"description": "Bundle and package orchestration CLI for JSKIT apps.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -20,9 +20,9 @@
|
|
|
20
20
|
"test": "node --test"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@jskit-ai/jskit-catalog": "0.1.
|
|
24
|
-
"@jskit-ai/kernel": "0.1.
|
|
25
|
-
"@jskit-ai/shell-web": "0.1.
|
|
23
|
+
"@jskit-ai/jskit-catalog": "0.1.122",
|
|
24
|
+
"@jskit-ai/kernel": "0.1.115",
|
|
25
|
+
"@jskit-ai/shell-web": "0.1.114",
|
|
26
26
|
"@vue/compiler-sfc": "^3.5.29",
|
|
27
27
|
"ts-morph": "^28.0.0"
|
|
28
28
|
},
|
|
@@ -29,6 +29,9 @@ import {
|
|
|
29
29
|
resolveTemplateContextReplacementsForMutation
|
|
30
30
|
} from "./templateContext.js";
|
|
31
31
|
import { normalizeMutationRelativeFilePath } from "./mutationPathUtils.js";
|
|
32
|
+
import {
|
|
33
|
+
isSensitiveTextMutation
|
|
34
|
+
} from "../sensitiveLockState.js";
|
|
32
35
|
|
|
33
36
|
const PRE_FILE_CONFIG_MUTATION_TARGETS = new Set([
|
|
34
37
|
"config/public.js",
|
|
@@ -83,17 +86,27 @@ async function applyTextMutations(
|
|
|
83
86
|
}
|
|
84
87
|
|
|
85
88
|
const recordKey = `${relativeFile}::${String(mutation?.id || resolvedKey)}`;
|
|
86
|
-
|
|
89
|
+
const mutationIsSensitive = isSensitiveTextMutation({
|
|
90
|
+
packageEntry,
|
|
91
|
+
mutation,
|
|
92
|
+
resolvedKey
|
|
93
|
+
});
|
|
94
|
+
const managedRecord = {
|
|
87
95
|
file: relativeFile,
|
|
88
96
|
op: "upsert-env",
|
|
89
97
|
key: resolvedKey,
|
|
90
|
-
value: resolvedValue,
|
|
91
98
|
hadPrevious: upserted.hadPrevious,
|
|
92
|
-
previousValue: upserted.previousValue,
|
|
93
99
|
reason: String(mutation?.reason || ""),
|
|
94
100
|
category: String(mutation?.category || ""),
|
|
95
101
|
id: String(mutation?.id || "")
|
|
96
102
|
};
|
|
103
|
+
if (mutationIsSensitive) {
|
|
104
|
+
managedRecord.sensitive = true;
|
|
105
|
+
} else {
|
|
106
|
+
managedRecord.value = resolvedValue;
|
|
107
|
+
managedRecord.previousValue = upserted.previousValue;
|
|
108
|
+
}
|
|
109
|
+
managedText[recordKey] = managedRecord;
|
|
97
110
|
touchedFiles.add(normalizeRelativePath(appRoot, absoluteFile));
|
|
98
111
|
continue;
|
|
99
112
|
}
|
|
@@ -158,10 +171,15 @@ async function applyTextMutations(
|
|
|
158
171
|
}
|
|
159
172
|
|
|
160
173
|
const recordKey = `${relativeFile}::${mutationId}`;
|
|
174
|
+
const mutationIsSensitive = isSensitiveTextMutation({
|
|
175
|
+
packageEntry,
|
|
176
|
+
mutation,
|
|
177
|
+
resolvedKey: ""
|
|
178
|
+
});
|
|
161
179
|
managedText[recordKey] = {
|
|
162
180
|
file: relativeFile,
|
|
163
181
|
op: "append-text",
|
|
164
|
-
value: renderedSnippet,
|
|
182
|
+
...(mutationIsSensitive ? { sensitive: true } : { value: renderedSnippet }),
|
|
165
183
|
position,
|
|
166
184
|
reason: String(mutation?.reason || ""),
|
|
167
185
|
category: String(mutation?.category || ""),
|
|
@@ -34,6 +34,9 @@ import {
|
|
|
34
34
|
import {
|
|
35
35
|
resolvePackageTemplateRoot
|
|
36
36
|
} from "./packageTemplateResolution.js";
|
|
37
|
+
import {
|
|
38
|
+
sanitizePackageOptionsForLock
|
|
39
|
+
} from "./sensitiveLockState.js";
|
|
37
40
|
import {
|
|
38
41
|
applyFileMutations,
|
|
39
42
|
applySourceMutations,
|
|
@@ -69,7 +72,7 @@ function createManagedRecordBase(packageEntry, options) {
|
|
|
69
72
|
files: [],
|
|
70
73
|
migrations: []
|
|
71
74
|
},
|
|
72
|
-
options,
|
|
75
|
+
options: sanitizePackageOptionsForLock(packageEntry, options),
|
|
73
76
|
installedAt: new Date().toISOString()
|
|
74
77
|
};
|
|
75
78
|
}
|
|
@@ -278,9 +281,7 @@ async function applyPackagePositioning({
|
|
|
278
281
|
version: packageEntry.version,
|
|
279
282
|
source: resolveManagedSourceRecord(packageEntry, existingInstall),
|
|
280
283
|
managed: nextManaged,
|
|
281
|
-
options:
|
|
282
|
-
...ensureObject(packageOptions)
|
|
283
|
-
},
|
|
284
|
+
options: sanitizePackageOptionsForLock(packageEntry, packageOptions),
|
|
284
285
|
installedAt: String(existingInstall.installedAt || new Date().toISOString())
|
|
285
286
|
};
|
|
286
287
|
lock.installedPackages[packageEntry.packageId] = managedRecord;
|
|
@@ -359,9 +360,7 @@ async function applyPackageMigrationsOnly({
|
|
|
359
360
|
packageId: packageEntry.packageId,
|
|
360
361
|
source: resolveManagedSourceRecord(packageEntry, existingInstall),
|
|
361
362
|
managed: nextManaged,
|
|
362
|
-
options:
|
|
363
|
-
...ensureObject(packageOptions)
|
|
364
|
-
},
|
|
363
|
+
options: sanitizePackageOptionsForLock(packageEntry, packageOptions),
|
|
365
364
|
migrationSyncVersion: packageEntry.version,
|
|
366
365
|
installedAt: String(existingInstall.installedAt || new Date().toISOString())
|
|
367
366
|
};
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import {
|
|
3
|
+
ensureArray,
|
|
4
|
+
ensureObject
|
|
5
|
+
} from "../shared/collectionUtils.js";
|
|
6
|
+
import {
|
|
7
|
+
escapeRegExp,
|
|
8
|
+
interpolateOptionValue,
|
|
9
|
+
isSecretOptionInput
|
|
10
|
+
} from "../shared/optionInterpolation.js";
|
|
11
|
+
import { parseEnvLineValue } from "./appState.js";
|
|
12
|
+
|
|
13
|
+
const OPTION_REFERENCE_PATTERN = /\$\{option:([a-z][a-z0-9-]*)(\|[^}]*)?\}/gi;
|
|
14
|
+
const SENSITIVE_NAME_PATTERN = /(^|[-_])(password|passwd|passphrase|secret|token|api[-_]?key|private[-_]?key|credential|credentials)([-_]|$)/i;
|
|
15
|
+
|
|
16
|
+
function collectOptionReferences(value = "") {
|
|
17
|
+
return [
|
|
18
|
+
...new Set(
|
|
19
|
+
[...String(value || "").matchAll(OPTION_REFERENCE_PATTERN)]
|
|
20
|
+
.map((match) => String(match[1] || "").trim())
|
|
21
|
+
.filter(Boolean)
|
|
22
|
+
)
|
|
23
|
+
];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function isSensitiveName(value = "") {
|
|
27
|
+
return SENSITIVE_NAME_PATTERN.test(String(value || "").trim());
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function isSensitivePackageOption(packageEntry = {}, optionName = "") {
|
|
31
|
+
const normalizedOptionName = String(optionName || "").trim();
|
|
32
|
+
if (!normalizedOptionName) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const optionSchemas = ensureObject(packageEntry?.descriptor?.options);
|
|
37
|
+
const optionSchema = ensureObject(optionSchemas[normalizedOptionName]);
|
|
38
|
+
return isSecretOptionInput(optionSchema) || isSensitiveName(normalizedOptionName);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function isSensitiveEnvKey(key = "") {
|
|
42
|
+
return isSensitiveName(String(key || "").trim().toLowerCase().replace(/_/g, "-"));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function textValueReferencesSensitiveOption(packageEntry = {}, value = "") {
|
|
46
|
+
return collectOptionReferences(value).some((optionName) =>
|
|
47
|
+
isSensitivePackageOption(packageEntry, optionName)
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function isExactOptionReference(value = "", optionName = "") {
|
|
52
|
+
const pattern = new RegExp(`^\\s*\\$\\{option:${escapeRegExp(optionName)}\\}\\s*$`, "i");
|
|
53
|
+
return pattern.test(String(value || ""));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function isSensitiveTextMutation({ packageEntry = {}, mutation = {}, resolvedKey = "" } = {}) {
|
|
57
|
+
const mutationRecord = ensureObject(mutation);
|
|
58
|
+
if (mutationRecord.sensitive === true || mutationRecord.secret === true) {
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
if (String(mutationRecord.op || "").trim() === "upsert-env" && isSensitiveEnvKey(resolvedKey)) {
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
return textValueReferencesSensitiveOption(packageEntry, mutationRecord.value);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function isSensitiveManagedTextRecord({ packageEntry = {}, record = {} } = {}) {
|
|
68
|
+
const changeRecord = ensureObject(record);
|
|
69
|
+
if (changeRecord.sensitive === true || changeRecord.secret === true) {
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
if (String(changeRecord.op || "").trim() === "upsert-env" && isSensitiveEnvKey(changeRecord.key)) {
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
return textValueReferencesSensitiveOption(packageEntry, changeRecord.value);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function sanitizePackageOptionsForLock(packageEntry = {}, options = {}) {
|
|
79
|
+
const sanitized = {};
|
|
80
|
+
for (const [optionName, optionValue] of Object.entries(ensureObject(options))) {
|
|
81
|
+
if (isSensitivePackageOption(packageEntry, optionName)) {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
sanitized[optionName] = optionValue;
|
|
85
|
+
}
|
|
86
|
+
return sanitized;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function sanitizePackageOptionsForResolve(packageEntry = {}, options = {}) {
|
|
90
|
+
return sanitizePackageOptionsForLock(packageEntry, options);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function sanitizeManagedTextForLock(packageEntry = {}, managedText = {}) {
|
|
94
|
+
const sanitized = {};
|
|
95
|
+
for (const [recordKey, rawRecord] of Object.entries(ensureObject(managedText))) {
|
|
96
|
+
const record = {
|
|
97
|
+
...ensureObject(rawRecord)
|
|
98
|
+
};
|
|
99
|
+
if (isSensitiveManagedTextRecord({ packageEntry, record })) {
|
|
100
|
+
delete record.value;
|
|
101
|
+
delete record.previousValue;
|
|
102
|
+
record.sensitive = true;
|
|
103
|
+
}
|
|
104
|
+
sanitized[recordKey] = record;
|
|
105
|
+
}
|
|
106
|
+
return sanitized;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function sanitizeInstalledPackageRecordForLock(record = {}, packageEntry = {}) {
|
|
110
|
+
const installedRecord = ensureObject(record);
|
|
111
|
+
const managed = ensureObject(installedRecord.managed);
|
|
112
|
+
const sanitizedRecord = {
|
|
113
|
+
...installedRecord,
|
|
114
|
+
options: sanitizePackageOptionsForLock(packageEntry, installedRecord.options)
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
if (Object.keys(managed).length > 0) {
|
|
118
|
+
sanitizedRecord.managed = {
|
|
119
|
+
...managed,
|
|
120
|
+
text: sanitizeManagedTextForLock(packageEntry, managed.text)
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return sanitizedRecord;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function sanitizeLockSecretsForWrite(lock = {}, packageRegistry = null) {
|
|
128
|
+
const lockRecord = ensureObject(lock);
|
|
129
|
+
const installedPackages = ensureObject(lockRecord.installedPackages);
|
|
130
|
+
for (const [packageId, installedRecord] of Object.entries(installedPackages)) {
|
|
131
|
+
const packageEntry = packageRegistry?.get?.(packageId) || {
|
|
132
|
+
packageId,
|
|
133
|
+
descriptor: {
|
|
134
|
+
options: {}
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
installedPackages[packageId] = sanitizeInstalledPackageRecordForLock(installedRecord, packageEntry);
|
|
138
|
+
}
|
|
139
|
+
lockRecord.installedPackages = installedPackages;
|
|
140
|
+
return lockRecord;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function readEnvValue(content = "", key = "") {
|
|
144
|
+
const lookupPattern = new RegExp(`^\\s*${escapeRegExp(key)}\\s*=`);
|
|
145
|
+
for (const line of String(content || "").split(/\r?\n/)) {
|
|
146
|
+
if (lookupPattern.test(line)) {
|
|
147
|
+
return String(parseEnvLineValue(line, key) || "");
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async function resolveSensitiveOptionEnvFallbacks({
|
|
154
|
+
packageEntry = {},
|
|
155
|
+
appRoot = "",
|
|
156
|
+
optionInput = {},
|
|
157
|
+
readFileBufferIfExists
|
|
158
|
+
} = {}) {
|
|
159
|
+
if (!appRoot || typeof readFileBufferIfExists !== "function") {
|
|
160
|
+
return {};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const fallbacks = {};
|
|
164
|
+
const runtimeOptions = ensureObject(optionInput);
|
|
165
|
+
const optionSchemas = ensureObject(packageEntry?.descriptor?.options);
|
|
166
|
+
const textMutations = ensureArray(ensureObject(packageEntry?.descriptor?.mutations).text);
|
|
167
|
+
|
|
168
|
+
for (const mutation of textMutations) {
|
|
169
|
+
const mutationRecord = ensureObject(mutation);
|
|
170
|
+
if (String(mutationRecord.op || "").trim() !== "upsert-env") {
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const relativeFile = String(mutationRecord.file || "").trim();
|
|
175
|
+
const rawKey = String(mutationRecord.key || "").trim();
|
|
176
|
+
if (!relativeFile || !rawKey) {
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const sensitiveOptionNames = collectOptionReferences(mutationRecord.value)
|
|
181
|
+
.filter((optionName) => isSensitivePackageOption(packageEntry, optionName))
|
|
182
|
+
.filter((optionName) => !Object.prototype.hasOwnProperty.call(runtimeOptions, optionName));
|
|
183
|
+
if (sensitiveOptionNames.length !== 1) {
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const optionName = sensitiveOptionNames[0];
|
|
188
|
+
if (!isExactOptionReference(mutationRecord.value, optionName)) {
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
let resolvedKey = "";
|
|
193
|
+
try {
|
|
194
|
+
resolvedKey = interpolateOptionValue(
|
|
195
|
+
rawKey,
|
|
196
|
+
runtimeOptions,
|
|
197
|
+
packageEntry.packageId,
|
|
198
|
+
`${rawKey}.key`
|
|
199
|
+
).trim();
|
|
200
|
+
} catch {
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
if (!resolvedKey) {
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const existing = await readFileBufferIfExists(path.join(appRoot, relativeFile));
|
|
208
|
+
if (!existing.exists) {
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const envValue = readEnvValue(existing.buffer.toString("utf8"), resolvedKey);
|
|
213
|
+
const optionSchema = ensureObject(optionSchemas[optionName]);
|
|
214
|
+
if (envValue !== null && (envValue || optionSchema.allowEmpty === true)) {
|
|
215
|
+
fallbacks[optionName] = envValue;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return fallbacks;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export {
|
|
223
|
+
collectOptionReferences,
|
|
224
|
+
isSensitiveEnvKey,
|
|
225
|
+
isSensitiveManagedTextRecord,
|
|
226
|
+
isSensitivePackageOption,
|
|
227
|
+
isSensitiveTextMutation,
|
|
228
|
+
resolveSensitiveOptionEnvFallbacks,
|
|
229
|
+
sanitizeInstalledPackageRecordForLock,
|
|
230
|
+
sanitizeLockSecretsForWrite,
|
|
231
|
+
sanitizeManagedTextForLock,
|
|
232
|
+
sanitizePackageOptionsForLock,
|
|
233
|
+
sanitizePackageOptionsForResolve
|
|
234
|
+
};
|
|
@@ -6,6 +6,11 @@ import {
|
|
|
6
6
|
ensureObject,
|
|
7
7
|
sortStrings
|
|
8
8
|
} from "../../shared/collectionUtils.js";
|
|
9
|
+
import {
|
|
10
|
+
resolveSensitiveOptionEnvFallbacks,
|
|
11
|
+
sanitizeLockSecretsForWrite,
|
|
12
|
+
sanitizePackageOptionsForResolve
|
|
13
|
+
} from "../../cliRuntime/sensitiveLockState.js";
|
|
9
14
|
import {
|
|
10
15
|
fileExists
|
|
11
16
|
} from "../appCommands/shared.js";
|
|
@@ -162,6 +167,35 @@ async function installAppDependenciesForHook({
|
|
|
162
167
|
}
|
|
163
168
|
}
|
|
164
169
|
|
|
170
|
+
async function resolvePackageOptionInputForInstall({
|
|
171
|
+
packageEntry,
|
|
172
|
+
existingInstall,
|
|
173
|
+
packageInlineOptions,
|
|
174
|
+
appRoot,
|
|
175
|
+
readFileBufferIfExists
|
|
176
|
+
}) {
|
|
177
|
+
const lockOptions = sanitizePackageOptionsForResolve(
|
|
178
|
+
packageEntry,
|
|
179
|
+
ensureObject(existingInstall.options)
|
|
180
|
+
);
|
|
181
|
+
const inlineOptions = ensureObject(packageInlineOptions);
|
|
182
|
+
const optionInput = {
|
|
183
|
+
...lockOptions,
|
|
184
|
+
...inlineOptions
|
|
185
|
+
};
|
|
186
|
+
const secretEnvFallbacks = await resolveSensitiveOptionEnvFallbacks({
|
|
187
|
+
packageEntry,
|
|
188
|
+
appRoot,
|
|
189
|
+
optionInput,
|
|
190
|
+
readFileBufferIfExists
|
|
191
|
+
});
|
|
192
|
+
return {
|
|
193
|
+
...lockOptions,
|
|
194
|
+
...secretEnvFallbacks,
|
|
195
|
+
...inlineOptions
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
165
199
|
function validateHookResult(result = {}, { packageId = "", hookLabel = "" } = {}) {
|
|
166
200
|
if (typeof result === "undefined" || result === null) {
|
|
167
201
|
return {};
|
|
@@ -327,6 +361,7 @@ async function runPackageAddCommand(ctx = {}, { positional, options, cwd, io })
|
|
|
327
361
|
applyPackageInstall,
|
|
328
362
|
adoptAppLocalPackageDependencies,
|
|
329
363
|
writeJsonFile,
|
|
364
|
+
readFileBufferIfExists,
|
|
330
365
|
runNpmInstall,
|
|
331
366
|
renderResolvedSummary,
|
|
332
367
|
createCatalogFetchStatusReporter = () => () => {}
|
|
@@ -572,13 +607,16 @@ async function runPackageAddCommand(ctx = {}, { positional, options, cwd, io })
|
|
|
572
607
|
: existingVersion === packageEntry.version
|
|
573
608
|
? "reapply"
|
|
574
609
|
: "upgrade";
|
|
575
|
-
const
|
|
610
|
+
const optionInput = await resolvePackageOptionInputForInstall({
|
|
611
|
+
packageEntry,
|
|
612
|
+
existingInstall,
|
|
613
|
+
packageInlineOptions,
|
|
614
|
+
appRoot,
|
|
615
|
+
readFileBufferIfExists
|
|
616
|
+
});
|
|
576
617
|
resolvedOptionsByPackage[packageId] = await resolvePackageOptions(
|
|
577
618
|
packageEntry,
|
|
578
|
-
|
|
579
|
-
...lockEntryOptions,
|
|
580
|
-
...packageInlineOptions
|
|
581
|
-
},
|
|
619
|
+
optionInput,
|
|
582
620
|
io,
|
|
583
621
|
{ appRoot }
|
|
584
622
|
);
|
|
@@ -759,6 +797,7 @@ async function runPackageAddCommand(ctx = {}, { positional, options, cwd, io })
|
|
|
759
797
|
|
|
760
798
|
if (!options.dryRun) {
|
|
761
799
|
await writeJsonFile(packageJsonPath, packageJson);
|
|
800
|
+
sanitizeLockSecretsForWrite(lock, combinedPackageRegistry);
|
|
762
801
|
await writeJsonFile(lockPath, lock);
|
|
763
802
|
if (options.runNpmInstall && !managesNpmInstall) {
|
|
764
803
|
await runNpmInstall(appRoot, io.stderr);
|
|
@@ -2,6 +2,9 @@ import {
|
|
|
2
2
|
ensureObject,
|
|
3
3
|
sortStrings
|
|
4
4
|
} from "../../shared/collectionUtils.js";
|
|
5
|
+
import {
|
|
6
|
+
sanitizeLockSecretsForWrite
|
|
7
|
+
} from "../../cliRuntime/sensitiveLockState.js";
|
|
5
8
|
|
|
6
9
|
async function runPackageCreateCommand(ctx = {}, { positional, options, cwd, io }) {
|
|
7
10
|
const {
|
|
@@ -111,6 +114,7 @@ async function runPackageCreateCommand(ctx = {}, { positional, options, cwd, io
|
|
|
111
114
|
const touchedFileList = sortStrings([...touchedFiles]);
|
|
112
115
|
if (!options.dryRun) {
|
|
113
116
|
await writeJsonFile(packageJsonPath, packageJson);
|
|
117
|
+
sanitizeLockSecretsForWrite(lock);
|
|
114
118
|
await writeJsonFile(lockPath, lock);
|
|
115
119
|
if (options.runNpmInstall) {
|
|
116
120
|
await runNpmInstall(appRoot, io.stderr);
|
|
@@ -3,6 +3,11 @@ import {
|
|
|
3
3
|
ensureObject,
|
|
4
4
|
sortStrings
|
|
5
5
|
} from "../../shared/collectionUtils.js";
|
|
6
|
+
import {
|
|
7
|
+
resolveSensitiveOptionEnvFallbacks,
|
|
8
|
+
sanitizeLockSecretsForWrite,
|
|
9
|
+
sanitizePackageOptionsForResolve
|
|
10
|
+
} from "../../cliRuntime/sensitiveLockState.js";
|
|
6
11
|
|
|
7
12
|
async function runPackageMigrationsCommand(ctx = {}, { positional, options, cwd, io }) {
|
|
8
13
|
const {
|
|
@@ -18,6 +23,7 @@ async function runPackageMigrationsCommand(ctx = {}, { positional, options, cwd,
|
|
|
18
23
|
validateInlineOptionsForPackage,
|
|
19
24
|
resolvePackageOptions,
|
|
20
25
|
applyPackageMigrationsOnly,
|
|
26
|
+
readFileBufferIfExists,
|
|
21
27
|
writeJsonFile
|
|
22
28
|
} = ctx;
|
|
23
29
|
|
|
@@ -84,10 +90,22 @@ async function runPackageMigrationsCommand(ctx = {}, { positional, options, cwd,
|
|
|
84
90
|
validateInlineOptionsForPackage(packageEntry, options.inlineOptions);
|
|
85
91
|
const installedRecord = ensureObject(installedPackages[packageId]);
|
|
86
92
|
const mergedInlineOptions = scope === "package" ? ensureObject(options.inlineOptions) : {};
|
|
93
|
+
const lockOptions = sanitizePackageOptionsForResolve(packageEntry, installedRecord.options);
|
|
94
|
+
const optionInput = {
|
|
95
|
+
...lockOptions,
|
|
96
|
+
...mergedInlineOptions
|
|
97
|
+
};
|
|
98
|
+
const secretEnvFallbacks = await resolveSensitiveOptionEnvFallbacks({
|
|
99
|
+
packageEntry,
|
|
100
|
+
appRoot,
|
|
101
|
+
optionInput,
|
|
102
|
+
readFileBufferIfExists
|
|
103
|
+
});
|
|
87
104
|
const resolvedOptions = await resolvePackageOptions(
|
|
88
105
|
packageEntry,
|
|
89
106
|
{
|
|
90
|
-
...
|
|
107
|
+
...lockOptions,
|
|
108
|
+
...secretEnvFallbacks,
|
|
91
109
|
...mergedInlineOptions
|
|
92
110
|
},
|
|
93
111
|
io,
|
|
@@ -114,6 +132,7 @@ async function runPackageMigrationsCommand(ctx = {}, { positional, options, cwd,
|
|
|
114
132
|
|
|
115
133
|
const touchedFileList = sortStrings([...touchedFiles]);
|
|
116
134
|
if (!options.dryRun) {
|
|
135
|
+
sanitizeLockSecretsForWrite(lock, combinedPackageRegistry);
|
|
117
136
|
await writeJsonFile(lockPath, lock);
|
|
118
137
|
}
|
|
119
138
|
|
|
@@ -2,6 +2,11 @@ import {
|
|
|
2
2
|
ensureObject,
|
|
3
3
|
sortStrings
|
|
4
4
|
} from "../../shared/collectionUtils.js";
|
|
5
|
+
import {
|
|
6
|
+
resolveSensitiveOptionEnvFallbacks,
|
|
7
|
+
sanitizeLockSecretsForWrite,
|
|
8
|
+
sanitizePackageOptionsForResolve
|
|
9
|
+
} from "../../cliRuntime/sensitiveLockState.js";
|
|
5
10
|
|
|
6
11
|
async function runPackagePositionCommand(ctx = {}, { positional, options, cwd, io }) {
|
|
7
12
|
const {
|
|
@@ -17,6 +22,7 @@ async function runPackagePositionCommand(ctx = {}, { positional, options, cwd, i
|
|
|
17
22
|
validateInlineOptionsForPackage,
|
|
18
23
|
resolvePackageOptions,
|
|
19
24
|
applyPackagePositioning,
|
|
25
|
+
readFileBufferIfExists,
|
|
20
26
|
writeJsonFile
|
|
21
27
|
} = ctx;
|
|
22
28
|
|
|
@@ -51,11 +57,24 @@ async function runPackagePositionCommand(ctx = {}, { positional, options, cwd, i
|
|
|
51
57
|
validateInlineOptionsForPackage(packageEntry, options.inlineOptions);
|
|
52
58
|
|
|
53
59
|
const installedRecord = ensureObject(installedPackages[resolvedTargetId]);
|
|
60
|
+
const lockOptions = sanitizePackageOptionsForResolve(packageEntry, installedRecord.options);
|
|
61
|
+
const inlineOptions = ensureObject(options.inlineOptions);
|
|
62
|
+
const optionInput = {
|
|
63
|
+
...lockOptions,
|
|
64
|
+
...inlineOptions
|
|
65
|
+
};
|
|
66
|
+
const secretEnvFallbacks = await resolveSensitiveOptionEnvFallbacks({
|
|
67
|
+
packageEntry,
|
|
68
|
+
appRoot,
|
|
69
|
+
optionInput,
|
|
70
|
+
readFileBufferIfExists
|
|
71
|
+
});
|
|
54
72
|
const resolvedOptions = await resolvePackageOptions(
|
|
55
73
|
packageEntry,
|
|
56
74
|
{
|
|
57
|
-
...
|
|
58
|
-
...
|
|
75
|
+
...lockOptions,
|
|
76
|
+
...secretEnvFallbacks,
|
|
77
|
+
...inlineOptions
|
|
59
78
|
},
|
|
60
79
|
io,
|
|
61
80
|
{ appRoot }
|
|
@@ -72,6 +91,7 @@ async function runPackagePositionCommand(ctx = {}, { positional, options, cwd, i
|
|
|
72
91
|
const touchedFileList = sortStrings([...touchedFiles]);
|
|
73
92
|
|
|
74
93
|
if (!options.dryRun) {
|
|
94
|
+
sanitizeLockSecretsForWrite(lock, combinedPackageRegistry);
|
|
75
95
|
await writeJsonFile(lockPath, lock);
|
|
76
96
|
}
|
|
77
97
|
|
|
@@ -3,6 +3,10 @@ import {
|
|
|
3
3
|
ensureObject,
|
|
4
4
|
sortStrings
|
|
5
5
|
} from "../../shared/collectionUtils.js";
|
|
6
|
+
import {
|
|
7
|
+
isSensitiveManagedTextRecord,
|
|
8
|
+
sanitizeLockSecretsForWrite
|
|
9
|
+
} from "../../cliRuntime/sensitiveLockState.js";
|
|
6
10
|
|
|
7
11
|
async function runPackageRemoveCommand(ctx = {}, { positional, options, cwd, io }) {
|
|
8
12
|
const {
|
|
@@ -61,6 +65,12 @@ async function runPackageRemoveCommand(ctx = {}, { positional, options, cwd, io
|
|
|
61
65
|
}
|
|
62
66
|
|
|
63
67
|
const lockEntry = ensureObject(installed[resolvedTargetId]);
|
|
68
|
+
const packageEntry = combinedPackageRegistry.get(resolvedTargetId) || {
|
|
69
|
+
packageId: resolvedTargetId,
|
|
70
|
+
descriptor: {
|
|
71
|
+
options: {}
|
|
72
|
+
}
|
|
73
|
+
};
|
|
64
74
|
const managed = ensureObject(lockEntry.managed);
|
|
65
75
|
const touchedFiles = new Set();
|
|
66
76
|
|
|
@@ -87,6 +97,9 @@ async function runPackageRemoveCommand(ctx = {}, { positional, options, cwd, io
|
|
|
87
97
|
if (String(changeRecord.op || "") !== "upsert-env") {
|
|
88
98
|
continue;
|
|
89
99
|
}
|
|
100
|
+
if (isSensitiveManagedTextRecord({ packageEntry, record: changeRecord })) {
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
90
103
|
const relativeFile = String(changeRecord.file || "").trim();
|
|
91
104
|
if (!relativeFile) {
|
|
92
105
|
continue;
|
|
@@ -150,6 +163,7 @@ async function runPackageRemoveCommand(ctx = {}, { positional, options, cwd, io
|
|
|
150
163
|
|
|
151
164
|
if (!options.dryRun) {
|
|
152
165
|
await writeJsonFile(packageJsonPath, packageJson);
|
|
166
|
+
sanitizeLockSecretsForWrite(lock, combinedPackageRegistry);
|
|
153
167
|
await writeJsonFile(lockPath, lock);
|
|
154
168
|
if (options.runNpmInstall) {
|
|
155
169
|
await runNpmInstall(appRoot, io.stderr);
|
|
@@ -540,7 +540,8 @@ const COMMAND_DESCRIPTORS = Object.freeze({
|
|
|
540
540
|
]),
|
|
541
541
|
defaults: Object.freeze([
|
|
542
542
|
"No npm install runs unless --run-npm-install is passed.",
|
|
543
|
-
"Existing lock options are reused unless overridden inline.",
|
|
543
|
+
"Existing non-secret lock options are reused unless overridden inline.",
|
|
544
|
+
"Secret options are read from current .env values or explicit inline options; they are not stored in the lock.",
|
|
544
545
|
"update reuses add package flow with forced reapply."
|
|
545
546
|
]),
|
|
546
547
|
fullUse: "jskit update package <packageId> [--<option> <value>...] [--dry-run] [--run-npm-install] [--json]",
|