@jskit-ai/jskit-cli 0.2.125 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jskit-ai/jskit-cli",
3
- "version": "0.2.125",
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.120",
24
- "@jskit-ai/kernel": "0.1.113",
25
- "@jskit-ai/shell-web": "0.1.112",
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
- managedText[recordKey] = {
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
  };
@@ -106,6 +106,10 @@ function normalizeResolvedOptionValue(value = "") {
106
106
  return String(value || "").trim().toLowerCase();
107
107
  }
108
108
 
109
+ function isFlagOptionSchema(schema = {}) {
110
+ return normalizeResolvedOptionValue(schema?.inputType) === "flag";
111
+ }
112
+
109
113
  function normalizeResolvedOptionSchemaValue({
110
114
  packageEntry,
111
115
  optionName = "",
@@ -475,6 +479,7 @@ async function resolvePackageOptions(packageEntry, inlineOptions, io, { appRoot
475
479
  for (const optionName of optionNames) {
476
480
  const schema = ensureObject(optionSchemas[optionName]);
477
481
  const allowEmpty = schema.allowEmpty === true;
482
+ const isFlagOption = isFlagOptionSchema(schema);
478
483
  const assignResolvedOption = (rawValue = "") => {
479
484
  const normalizedOptionValue = normalizeResolvedOptionSchemaValue({
480
485
  packageEntry,
@@ -489,7 +494,12 @@ async function resolvePackageOptions(packageEntry, inlineOptions, io, { appRoot
489
494
  return false;
490
495
  };
491
496
  if (hasInlineOption(optionName)) {
492
- const inlineValue = String(inlineOptionValues[optionName] || "").trim();
497
+ const rawInlineValue = inlineOptionValues[optionName];
498
+ const inlineValue = String(rawInlineValue ?? "").trim();
499
+ if (isFlagOption) {
500
+ assignResolvedOption(inlineValue || "true");
501
+ continue;
502
+ }
493
503
  if (inlineValue || allowEmpty) {
494
504
  assignResolvedOption(inlineValue);
495
505
  continue;
@@ -522,6 +532,11 @@ async function resolvePackageOptions(packageEntry, inlineOptions, io, { appRoot
522
532
  continue;
523
533
  }
524
534
 
535
+ if (isFlagOption) {
536
+ resolved[optionName] = "false";
537
+ continue;
538
+ }
539
+
525
540
  if (schema.required) {
526
541
  const promptConfigContext = appRoot ? await loadConfigContext() : {};
527
542
  assignResolvedOption(await promptForRequiredOption({
@@ -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 lockEntryOptions = ensureObject(existingInstall.options);
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
- ...ensureObject(installedRecord.options),
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
- ...ensureObject(installedRecord.options),
58
- ...ensureObject(options.inlineOptions)
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]",
@@ -525,6 +525,7 @@ export {
525
525
  appendTextSnippet,
526
526
  escapeRegExp,
527
527
  interpolateOptionValue,
528
+ isSecretOptionInput,
528
529
  normalizeSkipChecks,
529
530
  promptForRequiredOption
530
531
  };