diff --git a/npmDepsHash b/npmDepsHash index 31eaafd8..a2ef91a8 100644 --- a/npmDepsHash +++ b/npmDepsHash @@ -1 +1 @@ -sha256-NYzadQBEN6ZbZfdSnW9aoW77InHvL8oL0joXcrs1ktI= +sha256-Ud5IBToO1LXjvPkAyw+oc2m9EFbwtHvvyVO2Y2BdZwg= diff --git a/package-lock.json b/package-lock.json index 4e7afaf2..98834553 100644 --- a/package-lock.json +++ b/package-lock.json @@ -42,7 +42,7 @@ "nexpect": "^0.6.0", "node-gyp-build": "^4.4.0", "nodemon": "^3.0.1", - "polykey": "^1.17.4", + "polykey": "^1.18.0", "prettier": "^3.0.0", "shelljs": "^0.8.5", "shx": "^0.3.4", @@ -7602,9 +7602,9 @@ } }, "node_modules/polykey": { - "version": "1.17.4", - "resolved": "https://registry.npmjs.org/polykey/-/polykey-1.17.4.tgz", - "integrity": "sha512-uFOO/HnDfO8IanafPrhnrYS2bRA2TEzIr73YZwXWayVp9U+jyiX3G+Q0z3jLlv2AfAyruewPLBwRZE9noqZq9A==", + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/polykey/-/polykey-1.18.0.tgz", + "integrity": "sha512-WV34AVOz4s+hG+wJ6unNTHFu8SwlXWo4yKdU4Uas6v9ykX0hTkS80x64pRdx+hYv1DlYp1Rp4AEmaowtjIlWtA==", "dev": true, "dependencies": { "@matrixai/async-cancellable": "^1.1.1", diff --git a/package.json b/package.json index 233483ad..2463f446 100644 --- a/package.json +++ b/package.json @@ -153,7 +153,7 @@ "nexpect": "^0.6.0", "node-gyp-build": "^4.4.0", "nodemon": "^3.0.1", - "polykey": "^1.17.4", + "polykey": "^1.18.0", "prettier": "^3.0.0", "shelljs": "^0.8.5", "shx": "^0.3.4", diff --git a/src/secrets/CommandCat.ts b/src/secrets/CommandCat.ts index 78e5cbf8..84e49a92 100644 --- a/src/secrets/CommandCat.ts +++ b/src/secrets/CommandCat.ts @@ -123,7 +123,7 @@ class CommandGet extends CommandPolykey { break; default: never( - `Expected "SuccessMessage" or "ContentMessage", got ${type}`, + `Expected "SuccessMessage" or "ErrorMessage", got ${type}`, ); } } diff --git a/src/secrets/CommandCreate.ts b/src/secrets/CommandCreate.ts index 3de42a25..2476e197 100644 --- a/src/secrets/CommandCreate.ts +++ b/src/secrets/CommandCreate.ts @@ -1,4 +1,5 @@ import type PolykeyClient from 'polykey/dist/PolykeyClient'; +import path from 'path'; import * as errors from '../errors'; import CommandPolykey from '../CommandPolykey'; import * as binUtils from '../utils'; @@ -27,6 +28,7 @@ class CommandCreate extends CommandPolykey { const { default: PolykeyClient } = await import( 'polykey/dist/PolykeyClient' ); + const { never } = await import('polykey/dist/utils'); const clientOptions = await binProcessors.processClientOptions( options.nodePath, options.nodeId, @@ -68,16 +70,49 @@ class CommandCreate extends CommandPolykey { cause: e, }); } - await binUtils.retryAuthentication( - (auth) => - pkClient.rpcClient.methods.vaultsSecretsNew({ - metadata: auth, - nameOrId: secretPath[0], - secretName: secretPath[1] ?? '/', - secretContent: content.toString('binary'), - }), - meta, - ); + await binUtils.retryAuthentication(async (auth) => { + // Make sure the path exists + const response = + await pkClient.rpcClient.methods.vaultsSecretsMkdir(); + const writer = response.writable.getWriter(); + await writer.write({ + nameOrId: secretPath[0], + dirName: path.dirname(secretPath[1] ?? '/'), + metadata: { ...auth, options: { recursive: true } }, + }); + await writer.close(); + for await (const chunk of response.readable) { + const type = chunk.type; + switch (type) { + case 'SuccessMessage': + // No special action required if mkdir succeeds + break; + case 'ErrorMessage': + // This operation can only fail if a file already exists at the + // target location. No other error should happen. + if (chunk.code === 'EEXIST') { + throw new errors.ErrorPolykeyCLIMakeDirectory( + `A file already exists at path ${chunk.reason}`, + ); + } else { + throw new errors.ErrorPolykeyCLIMakeDirectory( + `Failed to create directory ${chunk.reason} (${chunk.code})`, + ); + } + default: + never( + `Expected "SuccessMessage" or "ErrorMessage", got ${type}`, + ); + } + } + // Write the contents + await pkClient.rpcClient.methods.vaultsSecretsWriteFile({ + metadata: auth, + nameOrId: secretPath[0], + secretName: secretPath[1] ?? '/', + secretContent: content.toString('binary'), + }); + }, meta); } finally { if (pkClient! != null) await pkClient.stop(); } diff --git a/src/secrets/CommandEdit.ts b/src/secrets/CommandEdit.ts index 716aa533..fc652d52 100644 --- a/src/secrets/CommandEdit.ts +++ b/src/secrets/CommandEdit.ts @@ -110,7 +110,7 @@ class CommandEdit extends CommandPolykey { break; default: never( - `Expected "SuccessMessage" or "ContentMessage", got ${type}`, + `Expected "SuccessMessage" or "ErrorMessage", got ${type}`, ); } } diff --git a/tests/secrets/create.test.ts b/tests/secrets/create.test.ts index 76b10708..17f84f25 100644 --- a/tests/secrets/create.test.ts +++ b/tests/secrets/create.test.ts @@ -93,7 +93,7 @@ describe('commandCreateSecret', () => { expect(result.exitCode).not.toBe(0); // The root directory is already defined so we can't create a new secret // at path `vault:/`. - expect(result.stderr).toInclude('ErrorSecretsSecretDefined'); + expect(result.stderr).toInclude('ErrorSecretsIsDirectory'); }); test.prop([fileNameArb, fileNameArb, envVariableArb], { numRuns: 10 })( 'secrets handle unix style paths for secrets',