@mondaydotcomorg/z2h-cli 0.25.3 → 0.25.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth.d.ts +2 -1
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +2 -1
- package/dist/commands/__tests__/build.test.d.ts +2 -0
- package/dist/commands/__tests__/build.test.d.ts.map +1 -0
- package/dist/commands/backend.d.ts.map +1 -1
- package/dist/commands/backend.js +7 -1
- package/dist/commands/build.d.ts.map +1 -1
- package/dist/commands/build.js +10 -0
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +42 -19
- package/dist/commands/grant.d.ts.map +1 -1
- package/dist/commands/grant.js +8 -7
- package/dist/esm/auth.d.ts +2 -1
- package/dist/esm/auth.d.ts.map +1 -1
- package/dist/esm/auth.mjs +2 -1
- package/dist/esm/commands/__tests__/build.test.d.ts +2 -0
- package/dist/esm/commands/__tests__/build.test.d.ts.map +1 -0
- package/dist/esm/commands/backend.d.ts.map +1 -1
- package/dist/esm/commands/backend.mjs +7 -1
- package/dist/esm/commands/build.d.ts.map +1 -1
- package/dist/esm/commands/build.mjs +10 -0
- package/dist/esm/commands/deploy.d.ts.map +1 -1
- package/dist/esm/commands/deploy.mjs +43 -20
- package/dist/esm/commands/grant.d.ts.map +1 -1
- package/dist/esm/commands/grant.mjs +8 -7
- package/dist/esm/index.mjs +41 -140
- package/dist/esm/types.d.ts +23 -0
- package/dist/esm/types.d.ts.map +1 -1
- package/dist/esm/util/__tests__/errors.test.d.ts +2 -0
- package/dist/esm/util/__tests__/errors.test.d.ts.map +1 -0
- package/dist/esm/util/__tests__/run-command.test.d.ts +2 -0
- package/dist/esm/util/__tests__/run-command.test.d.ts.map +1 -0
- package/dist/esm/util/auth/auth-store.d.ts +0 -4
- package/dist/esm/util/auth/auth-store.d.ts.map +1 -1
- package/dist/esm/util/auth/auth-store.mjs +2 -9
- package/dist/esm/util/auth/broker-credential-provider.d.ts.map +1 -1
- package/dist/esm/util/auth/broker-credential-provider.mjs +16 -16
- package/dist/esm/util/broker/app.d.ts.map +1 -1
- package/dist/esm/util/broker/app.mjs +17 -9
- package/dist/esm/util/errors.d.ts +48 -0
- package/dist/esm/util/errors.d.ts.map +1 -0
- package/dist/esm/util/errors.mjs +91 -0
- package/dist/esm/util/git/repo.d.ts +0 -3
- package/dist/esm/util/git/repo.d.ts.map +1 -1
- package/dist/esm/util/git/repo.mjs +4 -9
- package/dist/esm/util/logger.d.ts +4 -1
- package/dist/esm/util/logger.d.ts.map +1 -1
- package/dist/esm/util/logger.mjs +11 -3
- package/dist/esm/util/run-command.d.ts +16 -0
- package/dist/esm/util/run-command.d.ts.map +1 -0
- package/dist/esm/util/run-command.mjs +47 -0
- package/dist/esm/util/s3/upload.d.ts.map +1 -1
- package/dist/esm/util/s3/upload.mjs +22 -16
- package/dist/esm/util/tracker.d.ts +9 -2
- package/dist/esm/util/tracker.d.ts.map +1 -1
- package/dist/esm/util/tracker.mjs +18 -15
- package/dist/index.js +40 -139
- package/dist/types.d.ts +23 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/util/__tests__/errors.test.d.ts +2 -0
- package/dist/util/__tests__/errors.test.d.ts.map +1 -0
- package/dist/util/__tests__/run-command.test.d.ts +2 -0
- package/dist/util/__tests__/run-command.test.d.ts.map +1 -0
- package/dist/util/auth/auth-store.d.ts +0 -4
- package/dist/util/auth/auth-store.d.ts.map +1 -1
- package/dist/util/auth/auth-store.js +2 -10
- package/dist/util/auth/broker-credential-provider.d.ts.map +1 -1
- package/dist/util/auth/broker-credential-provider.js +16 -16
- package/dist/util/broker/app.d.ts.map +1 -1
- package/dist/util/broker/app.js +17 -9
- package/dist/util/errors.d.ts +48 -0
- package/dist/util/errors.d.ts.map +1 -0
- package/dist/util/errors.js +102 -0
- package/dist/util/git/repo.d.ts +0 -3
- package/dist/util/git/repo.d.ts.map +1 -1
- package/dist/util/git/repo.js +4 -10
- package/dist/util/logger.d.ts +4 -1
- package/dist/util/logger.d.ts.map +1 -1
- package/dist/util/logger.js +11 -3
- package/dist/util/run-command.d.ts +16 -0
- package/dist/util/run-command.d.ts.map +1 -0
- package/dist/util/run-command.js +49 -0
- package/dist/util/s3/upload.d.ts.map +1 -1
- package/dist/util/s3/upload.js +22 -16
- package/dist/util/tracker.d.ts +9 -2
- package/dist/util/tracker.d.ts.map +1 -1
- package/dist/util/tracker.js +19 -14
- package/handler-api/backend-runner/channels/llm-channel/llm.channel.ts +3 -2
- package/handler-api/backend-runner/channels/snowflake-channel/run-query.ts +7 -6
- package/handler-api/backend-runner/channels/snowflake-channel/snowflake.channel.ts +1 -1
- package/handler-api/backend-runner/channels/snowflake-channel/stateless-part-b.ts +1 -4
- package/package.json +2 -2
- package/src/auth.ts +2 -1
- package/src/commands/__tests__/backend-deploy.test.ts +18 -0
- package/src/commands/__tests__/build.test.ts +83 -0
- package/src/commands/__tests__/deploy.test.ts +107 -0
- package/src/commands/backend.ts +6 -1
- package/src/commands/build.ts +10 -0
- package/src/commands/deploy.ts +42 -20
- package/src/commands/grant.ts +8 -7
- package/src/index.ts +84 -133
- package/src/types.ts +47 -0
- package/src/util/__tests__/errors.test.ts +90 -0
- package/src/util/__tests__/run-command.test.ts +98 -0
- package/src/util/auth/__tests__/auth-store.test.ts +2 -1
- package/src/util/auth/__tests__/broker-credential-provider.test.ts +44 -8
- package/src/util/auth/auth-store.ts +1 -7
- package/src/util/auth/broker-credential-provider.ts +18 -16
- package/src/util/broker/__tests__/app.test.ts +23 -20
- package/src/util/broker/app.ts +16 -9
- package/src/util/errors.ts +98 -0
- package/src/util/git/__tests__/repo.test.ts +5 -3
- package/src/util/git/repo.ts +3 -9
- package/src/util/logger.ts +16 -3
- package/src/util/run-command.ts +59 -0
- package/src/util/s3/upload.ts +27 -22
- package/src/util/tracker.ts +19 -19
package/dist/auth.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export { generateZ2HTokenCommand, type GenerateZ2HTokenOptions } from './commands/generate-z2h-token';
|
|
2
|
-
export { readZthAuth,
|
|
2
|
+
export { readZthAuth, Z2H_AUTH_FILE, type ZthAuth } from './util/auth/auth-store';
|
|
3
|
+
export { Z2hAuthMissingError } from './util/errors';
|
|
3
4
|
//# sourceMappingURL=auth.d.ts.map
|
package/dist/auth.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,KAAK,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AACtG,OAAO,EAAE,WAAW,EAAE,
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,KAAK,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AACtG,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,KAAK,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAClF,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/auth.js
CHANGED
|
@@ -2,10 +2,11 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
2
2
|
|
|
3
3
|
const commands_generateZ2hToken = require('./commands/generate-z2h-token.js');
|
|
4
4
|
const util_auth_authStore = require('./util/auth/auth-store.js');
|
|
5
|
+
const util_errors = require('./util/errors.js');
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
exports.generateZ2HTokenCommand = commands_generateZ2hToken.generateZ2HTokenCommand;
|
|
9
10
|
exports.Z2H_AUTH_FILE = util_auth_authStore.Z2H_AUTH_FILE;
|
|
10
|
-
exports.Z2hAuthMissingError = util_auth_authStore.Z2hAuthMissingError;
|
|
11
11
|
exports.readZthAuth = util_auth_authStore.readZthAuth;
|
|
12
|
+
exports.Z2hAuthMissingError = util_errors.Z2hAuthMissingError;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build.test.d.ts","sourceRoot":"","sources":["../../../src/commands/__tests__/build.test.ts"],"names":[],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../../src/commands/backend.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../../src/commands/backend.ts"],"names":[],"mappings":"AAoBA;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAEnD;AAmCD;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;CACpB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CASpB;AAED,wBAAsB,oBAAoB,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAa5G"}
|
package/dist/commands/backend.js
CHANGED
|
@@ -10,6 +10,7 @@ const backend_detectIntegrations = require('../backend/detect-integrations.js');
|
|
|
10
10
|
const util_s3_client = require('../util/s3/client.js');
|
|
11
11
|
const constants = require('../constants.js');
|
|
12
12
|
const backend_client = require('../backend/client.js');
|
|
13
|
+
const util_errors = require('../util/errors.js');
|
|
13
14
|
|
|
14
15
|
const _interopDefault = e => e && e.__esModule ? e : { default: e };
|
|
15
16
|
|
|
@@ -75,7 +76,12 @@ async function deployBackend(opts) {
|
|
|
75
76
|
const { appDir, appName, mfKeyPrefix, version, deployedBy, isPreview } = opts;
|
|
76
77
|
const prefix = isPreview ? `${mfKeyPrefix.replace(/\/MF$/, '')}/MS` : `${appName}/backend-${version}`;
|
|
77
78
|
const logLabel = isPreview ? 'for preview' : `for "${appName}"`;
|
|
78
|
-
|
|
79
|
+
try {
|
|
80
|
+
return await readDetectAndUpload(appDir, prefix, version, deployedBy, logLabel);
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
throw util_errors.tagDeployError('deploy_backend_error', err);
|
|
84
|
+
}
|
|
79
85
|
}
|
|
80
86
|
async function backendInvokeCommand(handlerName, opts = {}) {
|
|
81
87
|
const appName = currentAppName();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/commands/build.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/commands/build.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE1D,MAAM,WAAW,YAAY;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,iBAAiB,CAAC;CAClC;AAGD,wBAAsB,YAAY,CAAC,IAAI,GAAE,YAAiB,GAAG,OAAO,CAAC,WAAW,CAAC,CAOhF"}
|
package/dist/commands/build.js
CHANGED
|
@@ -5,6 +5,7 @@ const fs = require('fs-extra');
|
|
|
5
5
|
const execa = require('execa');
|
|
6
6
|
const util_logger = require('../util/logger.js');
|
|
7
7
|
const shadow_prepare = require('../shadow/prepare.js');
|
|
8
|
+
const util_errors = require('../util/errors.js');
|
|
8
9
|
|
|
9
10
|
const _interopDefault = e => e && e.__esModule ? e : { default: e };
|
|
10
11
|
|
|
@@ -12,6 +13,15 @@ const path__default = /*#__PURE__*/_interopDefault(path);
|
|
|
12
13
|
|
|
13
14
|
// Should be run from the consumer app directory — uses process.cwd() to locate it.
|
|
14
15
|
async function buildCommand(opts = {}) {
|
|
16
|
+
try {
|
|
17
|
+
return await runBuild(opts);
|
|
18
|
+
}
|
|
19
|
+
catch (err) {
|
|
20
|
+
const signal = err instanceof Error ? err.signal : undefined;
|
|
21
|
+
throw new util_errors.DeployError(signal ? 'interrupted' : 'build_error', util_errors.messageOf(err));
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
async function runBuild(opts) {
|
|
15
25
|
const ctx = await shadow_prepare.prepareShadow();
|
|
16
26
|
// Typecheck against the shadow's tsconfig (the consumer dir has none).
|
|
17
27
|
// Errors fail the build before we waste time on the Trident pipeline.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAE,KAAK,UAAU,EAA2C,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAE,KAAK,UAAU,EAA2C,MAAM,4BAA4B,CAAC;AAkBtG,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAWD,wBAAsB,aAAa,CAAC,IAAI,GAAE,aAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAyB3E"}
|
package/dist/commands/deploy.js
CHANGED
|
@@ -18,6 +18,7 @@ const util_machineIdentity = require('../util/machine-identity.js');
|
|
|
18
18
|
const util_broker_app = require('../util/broker/app.js');
|
|
19
19
|
const util_openBrowser = require('../util/open-browser.js');
|
|
20
20
|
const shadow_prepare = require('../shadow/prepare.js');
|
|
21
|
+
const util_errors = require('../util/errors.js');
|
|
21
22
|
const commands_backend = require('./backend.js');
|
|
22
23
|
const commands_build = require('./build.js');
|
|
23
24
|
const commands_tag = require('./tag.js');
|
|
@@ -41,7 +42,13 @@ async function deployCommand(opts = {}) {
|
|
|
41
42
|
const appName = util_appName.mfAppName(consumerPkg.name);
|
|
42
43
|
// Validate the user's deploy token is on disk before doing any expensive
|
|
43
44
|
// work. Throws Z2hAuthMissingError with a remediation hint if not.
|
|
44
|
-
|
|
45
|
+
let token;
|
|
46
|
+
try {
|
|
47
|
+
({ token } = await util_auth_authStore.readZthAuth());
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
throw util_errors.tagDeployError('read_auth_token', err);
|
|
51
|
+
}
|
|
45
52
|
const ctx = { paths, consumerName: consumerPkg.name, appName, token, opts };
|
|
46
53
|
if (opts.preview) {
|
|
47
54
|
await deployPreview(ctx);
|
|
@@ -98,6 +105,8 @@ async function deployPreview({ paths, consumerName, appName, token, opts }) {
|
|
|
98
105
|
// Full CDN URL, not a relative path — trident-skeleton uses a manifest
|
|
99
106
|
// entry's `url` verbatim as the asset base, never joined with its own mfUrl.
|
|
100
107
|
const url = `${constants.BUCKET_READ_DNS_URL}/${keyPrefix}`;
|
|
108
|
+
// No catch-all: every stage below tags its own DeployError reason at the throw site,
|
|
109
|
+
// and anything unclassified reports as `unknown` via reasonOf() in runCommand.
|
|
101
110
|
const nextVersion = (await util_auth_brokerCredentialProvider.fetchDeployVersion(previewAppName)) + 1;
|
|
102
111
|
util_logger.info('building');
|
|
103
112
|
const entry = await buildAndStamp({ paths, publicUrl: url, nextVersion, gitRemote: '' });
|
|
@@ -140,10 +149,10 @@ async function deployLive({ paths, consumerName, appName, token, opts }) {
|
|
|
140
149
|
else {
|
|
141
150
|
if (!opts.remote) {
|
|
142
151
|
throw new Error(`This app has no git remote. On first deploy, tell Z2H how to track source:\n\n` +
|
|
143
|
-
` z2h-cli deploy --remote s3 (default)\n` +
|
|
144
|
-
`
|
|
152
|
+
` z2h-cli deploy --remote s3 (default, recommended)\n` +
|
|
153
|
+
` We store and back up your code for you automatically. No extra setup.\n\n` +
|
|
145
154
|
` z2h-cli deploy --remote github --repo-url https://github.com/org/repo.git\n` +
|
|
146
|
-
`
|
|
155
|
+
` Manage it yourself in an existing GitHub repo (must exist before deploying).`);
|
|
147
156
|
}
|
|
148
157
|
util_git_remoteOptions.validateRemoteOptions(opts);
|
|
149
158
|
// Pass the raw consumer name (not the mf-prefixed appName) so defaultS3Remote
|
|
@@ -160,9 +169,14 @@ async function deployLive({ paths, consumerName, appName, token, opts }) {
|
|
|
160
169
|
// The skill commits the working tree (with an LLM-authored message) before
|
|
161
170
|
// invoking deploy, so the tree should be clean here.
|
|
162
171
|
if (gitRemote) {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
172
|
+
try {
|
|
173
|
+
await util_git_repo.guardCleanTree(process.cwd());
|
|
174
|
+
util_logger.info('syncing with git remote');
|
|
175
|
+
await util_git_repo.pullAndResolve(process.cwd(), gitEnv);
|
|
176
|
+
}
|
|
177
|
+
catch (err) {
|
|
178
|
+
throw util_errors.tagDeployError('git_error', err);
|
|
179
|
+
}
|
|
166
180
|
}
|
|
167
181
|
util_logger.info('reading current Z2H manifest');
|
|
168
182
|
const keyPrefix = `${appName}/${nextVersion}`;
|
|
@@ -182,7 +196,6 @@ async function deployLive({ paths, consumerName, appName, token, opts }) {
|
|
|
182
196
|
});
|
|
183
197
|
return;
|
|
184
198
|
}
|
|
185
|
-
// lock → git push → bundle upload → manifest PUT (the manifest write is the single commit point).
|
|
186
199
|
try {
|
|
187
200
|
// Acquire the deploy lock explicitly. Done after the build
|
|
188
201
|
// so the lock hold stays tiny — it wraps only push → upload → manifest.
|
|
@@ -211,6 +224,8 @@ async function deployLive({ paths, consumerName, appName, token, opts }) {
|
|
|
211
224
|
});
|
|
212
225
|
}
|
|
213
226
|
finally {
|
|
227
|
+
// Same as preview: each stage tags its own reason, so no catch-all here — the try
|
|
228
|
+
// exists only so the lock is released on every exit path.
|
|
214
229
|
util_logger.info('releasing deploy lock');
|
|
215
230
|
await releaseLockBestEffort(appName, token);
|
|
216
231
|
}
|
|
@@ -256,31 +271,39 @@ async function handleDeployTags(appSlug) {
|
|
|
256
271
|
util_logger.warn(`could not update tags: ${err.message}`);
|
|
257
272
|
}
|
|
258
273
|
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
async function acquireDeployLock(appName, token) {
|
|
262
|
-
const res = await fetch(`${constants.brokerBaseUrl()}/z2h-cli/lock/acquire`, {
|
|
274
|
+
function postToLockEndpoint(path, appName, token) {
|
|
275
|
+
return fetch(`${constants.brokerBaseUrl()}${path}`, {
|
|
263
276
|
method: 'POST',
|
|
264
277
|
headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json', ...constants.routingHeaders() },
|
|
265
278
|
body: JSON.stringify({ appName }),
|
|
266
279
|
});
|
|
280
|
+
}
|
|
281
|
+
// Acquire the per-app deploy lock. Throws a friendly error on 409 (someone else is deploying)
|
|
282
|
+
// so the deploy stops and the caller can retry; the backend TTL backstops a crashed holder.
|
|
283
|
+
async function acquireDeployLock(appName, token) {
|
|
284
|
+
let res;
|
|
285
|
+
try {
|
|
286
|
+
res = await postToLockEndpoint('/z2h-cli/lock/acquire', appName, token);
|
|
287
|
+
}
|
|
288
|
+
catch (err) {
|
|
289
|
+
throw util_errors.tagDeployError('broker_error', err);
|
|
290
|
+
}
|
|
267
291
|
if (res.status === 409) {
|
|
268
|
-
throw new
|
|
292
|
+
throw new util_errors.DeployError('lock_timeout', `Someone is deploying "${appName}" right now — try again in a moment.`);
|
|
269
293
|
}
|
|
270
294
|
if (!res.ok) {
|
|
295
|
+
// Only 409 is contention — don't report a broker outage or a stale token as lock contention.
|
|
271
296
|
const text = await res.text();
|
|
272
|
-
|
|
297
|
+
const grantReason = util_errors.grantReasonForStatus(res.status);
|
|
298
|
+
const reason = grantReason === 'auth' || grantReason === 'forbidden' ? 'broker_auth' : 'broker_error';
|
|
299
|
+
throw new util_errors.DeployError(reason, `Failed to acquire deploy lock (${res.status}): ${text.slice(0, 512)}`);
|
|
273
300
|
}
|
|
274
301
|
util_logger.info(`deploy lock acquired for ${appName}`);
|
|
275
302
|
}
|
|
276
303
|
// best-effort — the 2-min TTL backstop handles failures
|
|
277
304
|
async function releaseLockBestEffort(appName, token) {
|
|
278
305
|
try {
|
|
279
|
-
const res = await
|
|
280
|
-
method: 'POST',
|
|
281
|
-
headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json', ...constants.routingHeaders() },
|
|
282
|
-
body: JSON.stringify({ appName }),
|
|
283
|
-
});
|
|
306
|
+
const res = await postToLockEndpoint('/z2h-cli/lock/release', appName, token);
|
|
284
307
|
if (!res.ok) {
|
|
285
308
|
util_logger.warn(`failed to release lock for ${appName} (${res.status})`);
|
|
286
309
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"grant.d.ts","sourceRoot":"","sources":["../../src/commands/grant.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"grant.d.ts","sourceRoot":"","sources":["../../src/commands/grant.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC;CACvC;AAkBD,wBAAsB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA4B/F;AAED,wBAAsB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBhG;AAED,wBAAsB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAmBnF"}
|
package/dist/commands/grant.js
CHANGED
|
@@ -4,6 +4,7 @@ const util_logger = require('../util/logger.js');
|
|
|
4
4
|
const util_appName = require('../util/app-name.js');
|
|
5
5
|
const constants = require('../constants.js');
|
|
6
6
|
const util_auth_authStore = require('../util/auth/auth-store.js');
|
|
7
|
+
const util_errors = require('../util/errors.js');
|
|
7
8
|
|
|
8
9
|
async function postGrant(appName, token, body) {
|
|
9
10
|
const res = await fetch(`${constants.brokerBaseUrl()}/z2h-apps/${appName}/grants`, {
|
|
@@ -13,16 +14,16 @@ async function postGrant(appName, token, body) {
|
|
|
13
14
|
});
|
|
14
15
|
if (!res.ok) {
|
|
15
16
|
const text = await res.text();
|
|
16
|
-
throw new
|
|
17
|
+
throw new util_errors.GrantError(res.status, `Grant failed (${res.status}): ${text.slice(0, 1024)}`);
|
|
17
18
|
}
|
|
18
19
|
}
|
|
19
20
|
async function grantCommand(email, opts) {
|
|
20
21
|
if (!opts.public && !email) {
|
|
21
|
-
throw new
|
|
22
|
+
throw new util_errors.GrantError('validation', 'Specify what to grant — e.g. --public or provide an email');
|
|
22
23
|
}
|
|
23
24
|
const level = opts.level ?? 'viewer';
|
|
24
25
|
if (!['owner', 'editor', 'viewer'].includes(level)) {
|
|
25
|
-
throw new
|
|
26
|
+
throw new util_errors.GrantError('validation', `Invalid --level "${level}". Must be owner, editor, or viewer`);
|
|
26
27
|
}
|
|
27
28
|
const appName = util_appName.mfAppName(util_appName.getCwdAppName());
|
|
28
29
|
const { token } = await util_auth_authStore.readZthAuth();
|
|
@@ -43,7 +44,7 @@ async function grantCommand(email, opts) {
|
|
|
43
44
|
}
|
|
44
45
|
async function revokeCommand(email, opts) {
|
|
45
46
|
if (!opts.public && !email) {
|
|
46
|
-
throw new
|
|
47
|
+
throw new util_errors.GrantError('validation', 'Specify what to revoke — e.g. --public or provide an email');
|
|
47
48
|
}
|
|
48
49
|
const appName = util_appName.mfAppName(util_appName.getCwdAppName());
|
|
49
50
|
const { token } = await util_auth_authStore.readZthAuth();
|
|
@@ -54,7 +55,7 @@ async function revokeCommand(email, opts) {
|
|
|
54
55
|
});
|
|
55
56
|
if (!res.ok) {
|
|
56
57
|
const text = await res.text();
|
|
57
|
-
throw new
|
|
58
|
+
throw new util_errors.GrantError(res.status, `Revoke failed (${res.status}): ${text.slice(0, 1024)}`);
|
|
58
59
|
}
|
|
59
60
|
const revoked = email ?? 'public';
|
|
60
61
|
const msg = email
|
|
@@ -64,7 +65,7 @@ async function revokeCommand(email, opts) {
|
|
|
64
65
|
}
|
|
65
66
|
async function transferOwnerCommand(email) {
|
|
66
67
|
if (!email) {
|
|
67
|
-
throw new
|
|
68
|
+
throw new util_errors.GrantError('validation', 'Specify the new owner — e.g. z2h-cli transfer-owner someone@monday.com');
|
|
68
69
|
}
|
|
69
70
|
const appName = util_appName.mfAppName(util_appName.getCwdAppName());
|
|
70
71
|
const { token } = await util_auth_authStore.readZthAuth();
|
|
@@ -75,7 +76,7 @@ async function transferOwnerCommand(email) {
|
|
|
75
76
|
});
|
|
76
77
|
if (!res.ok) {
|
|
77
78
|
const text = await res.text();
|
|
78
|
-
throw new
|
|
79
|
+
throw new util_errors.GrantError(res.status, `Transfer owner failed (${res.status}): ${text.slice(0, 1024)}`);
|
|
79
80
|
}
|
|
80
81
|
util_logger.output(`[z2h-cli] "${appName}" ownership transferred to ${email}`, { ok: true, appName, newOwner: email });
|
|
81
82
|
}
|
package/dist/esm/auth.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export { generateZ2HTokenCommand, type GenerateZ2HTokenOptions } from './commands/generate-z2h-token';
|
|
2
|
-
export { readZthAuth,
|
|
2
|
+
export { readZthAuth, Z2H_AUTH_FILE, type ZthAuth } from './util/auth/auth-store';
|
|
3
|
+
export { Z2hAuthMissingError } from './util/errors';
|
|
3
4
|
//# sourceMappingURL=auth.d.ts.map
|
package/dist/esm/auth.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,KAAK,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AACtG,OAAO,EAAE,WAAW,EAAE,
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,KAAK,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AACtG,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,KAAK,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAClF,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/esm/auth.mjs
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
export { generateZ2HTokenCommand } from './commands/generate-z2h-token.mjs';
|
|
2
|
-
export { Z2H_AUTH_FILE,
|
|
2
|
+
export { Z2H_AUTH_FILE, readZthAuth } from './util/auth/auth-store.mjs';
|
|
3
|
+
export { Z2hAuthMissingError } from './util/errors.mjs';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build.test.d.ts","sourceRoot":"","sources":["../../../../src/commands/__tests__/build.test.ts"],"names":[],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../../../src/commands/backend.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../../../src/commands/backend.ts"],"names":[],"mappings":"AAoBA;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAEnD;AAmCD;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;CACpB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CASpB;AAED,wBAAsB,oBAAoB,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAa5G"}
|
|
@@ -8,6 +8,7 @@ import { detectIntegrationsFromHandlers } from '../backend/detect-integrations.m
|
|
|
8
8
|
import { getS3Client } from '../util/s3/client.mjs';
|
|
9
9
|
import { BUCKET_WRITE_NAME } from '../constants.mjs';
|
|
10
10
|
import { invokeBackend } from '../backend/client.mjs';
|
|
11
|
+
import { tagDeployError } from '../util/errors.mjs';
|
|
11
12
|
|
|
12
13
|
function currentAppName() {
|
|
13
14
|
const raw = getCwdAppName();
|
|
@@ -69,7 +70,12 @@ async function deployBackend(opts) {
|
|
|
69
70
|
const { appDir, appName, mfKeyPrefix, version, deployedBy, isPreview } = opts;
|
|
70
71
|
const prefix = isPreview ? `${mfKeyPrefix.replace(/\/MF$/, '')}/MS` : `${appName}/backend-${version}`;
|
|
71
72
|
const logLabel = isPreview ? 'for preview' : `for "${appName}"`;
|
|
72
|
-
|
|
73
|
+
try {
|
|
74
|
+
return await readDetectAndUpload(appDir, prefix, version, deployedBy, logLabel);
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
throw tagDeployError('deploy_backend_error', err);
|
|
78
|
+
}
|
|
73
79
|
}
|
|
74
80
|
async function backendInvokeCommand(handlerName, opts = {}) {
|
|
75
81
|
const appName = currentAppName();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../../src/commands/build.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../../src/commands/build.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE1D,MAAM,WAAW,YAAY;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,iBAAiB,CAAC;CAClC;AAGD,wBAAsB,YAAY,CAAC,IAAI,GAAE,YAAiB,GAAG,OAAO,CAAC,WAAW,CAAC,CAOhF"}
|
|
@@ -3,9 +3,19 @@ import { pathExists, readJson } from 'fs-extra';
|
|
|
3
3
|
import { execa } from 'execa';
|
|
4
4
|
import { info, output } from '../util/logger.mjs';
|
|
5
5
|
import { prepareShadow } from '../shadow/prepare.mjs';
|
|
6
|
+
import { DeployError, messageOf } from '../util/errors.mjs';
|
|
6
7
|
|
|
7
8
|
// Should be run from the consumer app directory — uses process.cwd() to locate it.
|
|
8
9
|
async function buildCommand(opts = {}) {
|
|
10
|
+
try {
|
|
11
|
+
return await runBuild(opts);
|
|
12
|
+
}
|
|
13
|
+
catch (err) {
|
|
14
|
+
const signal = err instanceof Error ? err.signal : undefined;
|
|
15
|
+
throw new DeployError(signal ? 'interrupted' : 'build_error', messageOf(err));
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
async function runBuild(opts) {
|
|
9
19
|
const ctx = await prepareShadow();
|
|
10
20
|
// Typecheck against the shadow's tsconfig (the consumer dir has none).
|
|
11
21
|
// Errors fail the build before we waste time on the Trident pipeline.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../../src/commands/deploy.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAE,KAAK,UAAU,EAA2C,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../../src/commands/deploy.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAE,KAAK,UAAU,EAA2C,MAAM,4BAA4B,CAAC;AAkBtG,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAWD,wBAAsB,aAAa,CAAC,IAAI,GAAE,aAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAyB3E"}
|
|
@@ -4,7 +4,7 @@ import { info, output, getOutputMode, warn } from '../util/logger.mjs';
|
|
|
4
4
|
import { resolveConsumerPaths } from '../shadow/paths.mjs';
|
|
5
5
|
import { fetchAppTags, fetchTagCatalog, setAppTags } from '../util/tags.mjs';
|
|
6
6
|
import { uploadDir } from '../util/s3/upload.mjs';
|
|
7
|
-
import { BUCKET_WRITE_NAME,
|
|
7
|
+
import { BUCKET_WRITE_NAME, S3_PREVIEWS_FOLDER, BUCKET_READ_DNS_URL, brokerBaseUrl, routingHeaders } from '../constants.mjs';
|
|
8
8
|
import { fetchDeployVersion, brokerCredentialProvider } from '../util/auth/broker-credential-provider.mjs';
|
|
9
9
|
import { readZthAuth } from '../util/auth/auth-store.mjs';
|
|
10
10
|
import { validateAppName, cleanAppName, mfAppName, buildPreviewSlug } from '../util/app-name.mjs';
|
|
@@ -16,6 +16,7 @@ import { getMachineAppName } from '../util/machine-identity.mjs';
|
|
|
16
16
|
import { registerOrUpdateApp } from '../util/broker/app.mjs';
|
|
17
17
|
import { openInBrowser } from '../util/open-browser.mjs';
|
|
18
18
|
import { resolvePortAndMetadata } from '../shadow/prepare.mjs';
|
|
19
|
+
import { tagDeployError, DeployError, grantReasonForStatus } from '../util/errors.mjs';
|
|
19
20
|
import { deployBackend } from './backend.mjs';
|
|
20
21
|
import { buildCommand } from './build.mjs';
|
|
21
22
|
import { promptForTags } from './tag.mjs';
|
|
@@ -35,7 +36,13 @@ async function deployCommand(opts = {}) {
|
|
|
35
36
|
const appName = mfAppName(consumerPkg.name);
|
|
36
37
|
// Validate the user's deploy token is on disk before doing any expensive
|
|
37
38
|
// work. Throws Z2hAuthMissingError with a remediation hint if not.
|
|
38
|
-
|
|
39
|
+
let token;
|
|
40
|
+
try {
|
|
41
|
+
({ token } = await readZthAuth());
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
throw tagDeployError('read_auth_token', err);
|
|
45
|
+
}
|
|
39
46
|
const ctx = { paths, consumerName: consumerPkg.name, appName, token, opts };
|
|
40
47
|
if (opts.preview) {
|
|
41
48
|
await deployPreview(ctx);
|
|
@@ -92,6 +99,8 @@ async function deployPreview({ paths, consumerName, appName, token, opts }) {
|
|
|
92
99
|
// Full CDN URL, not a relative path — trident-skeleton uses a manifest
|
|
93
100
|
// entry's `url` verbatim as the asset base, never joined with its own mfUrl.
|
|
94
101
|
const url = `${BUCKET_READ_DNS_URL}/${keyPrefix}`;
|
|
102
|
+
// No catch-all: every stage below tags its own DeployError reason at the throw site,
|
|
103
|
+
// and anything unclassified reports as `unknown` via reasonOf() in runCommand.
|
|
95
104
|
const nextVersion = (await fetchDeployVersion(previewAppName)) + 1;
|
|
96
105
|
info('building');
|
|
97
106
|
const entry = await buildAndStamp({ paths, publicUrl: url, nextVersion, gitRemote: '' });
|
|
@@ -134,10 +143,10 @@ async function deployLive({ paths, consumerName, appName, token, opts }) {
|
|
|
134
143
|
else {
|
|
135
144
|
if (!opts.remote) {
|
|
136
145
|
throw new Error(`This app has no git remote. On first deploy, tell Z2H how to track source:\n\n` +
|
|
137
|
-
` z2h-cli deploy --remote s3 (default)\n` +
|
|
138
|
-
`
|
|
146
|
+
` z2h-cli deploy --remote s3 (default, recommended)\n` +
|
|
147
|
+
` We store and back up your code for you automatically. No extra setup.\n\n` +
|
|
139
148
|
` z2h-cli deploy --remote github --repo-url https://github.com/org/repo.git\n` +
|
|
140
|
-
`
|
|
149
|
+
` Manage it yourself in an existing GitHub repo (must exist before deploying).`);
|
|
141
150
|
}
|
|
142
151
|
validateRemoteOptions(opts);
|
|
143
152
|
// Pass the raw consumer name (not the mf-prefixed appName) so defaultS3Remote
|
|
@@ -154,9 +163,14 @@ async function deployLive({ paths, consumerName, appName, token, opts }) {
|
|
|
154
163
|
// The skill commits the working tree (with an LLM-authored message) before
|
|
155
164
|
// invoking deploy, so the tree should be clean here.
|
|
156
165
|
if (gitRemote) {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
166
|
+
try {
|
|
167
|
+
await guardCleanTree(process.cwd());
|
|
168
|
+
info('syncing with git remote');
|
|
169
|
+
await pullAndResolve(process.cwd(), gitEnv);
|
|
170
|
+
}
|
|
171
|
+
catch (err) {
|
|
172
|
+
throw tagDeployError('git_error', err);
|
|
173
|
+
}
|
|
160
174
|
}
|
|
161
175
|
info('reading current Z2H manifest');
|
|
162
176
|
const keyPrefix = `${appName}/${nextVersion}`;
|
|
@@ -176,7 +190,6 @@ async function deployLive({ paths, consumerName, appName, token, opts }) {
|
|
|
176
190
|
});
|
|
177
191
|
return;
|
|
178
192
|
}
|
|
179
|
-
// lock → git push → bundle upload → manifest PUT (the manifest write is the single commit point).
|
|
180
193
|
try {
|
|
181
194
|
// Acquire the deploy lock explicitly. Done after the build
|
|
182
195
|
// so the lock hold stays tiny — it wraps only push → upload → manifest.
|
|
@@ -205,6 +218,8 @@ async function deployLive({ paths, consumerName, appName, token, opts }) {
|
|
|
205
218
|
});
|
|
206
219
|
}
|
|
207
220
|
finally {
|
|
221
|
+
// Same as preview: each stage tags its own reason, so no catch-all here — the try
|
|
222
|
+
// exists only so the lock is released on every exit path.
|
|
208
223
|
info('releasing deploy lock');
|
|
209
224
|
await releaseLockBestEffort(appName, token);
|
|
210
225
|
}
|
|
@@ -250,31 +265,39 @@ async function handleDeployTags(appSlug) {
|
|
|
250
265
|
warn(`could not update tags: ${err.message}`);
|
|
251
266
|
}
|
|
252
267
|
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
async function acquireDeployLock(appName, token) {
|
|
256
|
-
const res = await fetch(`${brokerBaseUrl()}/z2h-cli/lock/acquire`, {
|
|
268
|
+
function postToLockEndpoint(path, appName, token) {
|
|
269
|
+
return fetch(`${brokerBaseUrl()}${path}`, {
|
|
257
270
|
method: 'POST',
|
|
258
271
|
headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json', ...routingHeaders() },
|
|
259
272
|
body: JSON.stringify({ appName }),
|
|
260
273
|
});
|
|
274
|
+
}
|
|
275
|
+
// Acquire the per-app deploy lock. Throws a friendly error on 409 (someone else is deploying)
|
|
276
|
+
// so the deploy stops and the caller can retry; the backend TTL backstops a crashed holder.
|
|
277
|
+
async function acquireDeployLock(appName, token) {
|
|
278
|
+
let res;
|
|
279
|
+
try {
|
|
280
|
+
res = await postToLockEndpoint('/z2h-cli/lock/acquire', appName, token);
|
|
281
|
+
}
|
|
282
|
+
catch (err) {
|
|
283
|
+
throw tagDeployError('broker_error', err);
|
|
284
|
+
}
|
|
261
285
|
if (res.status === 409) {
|
|
262
|
-
throw new
|
|
286
|
+
throw new DeployError('lock_timeout', `Someone is deploying "${appName}" right now — try again in a moment.`);
|
|
263
287
|
}
|
|
264
288
|
if (!res.ok) {
|
|
289
|
+
// Only 409 is contention — don't report a broker outage or a stale token as lock contention.
|
|
265
290
|
const text = await res.text();
|
|
266
|
-
|
|
291
|
+
const grantReason = grantReasonForStatus(res.status);
|
|
292
|
+
const reason = grantReason === 'auth' || grantReason === 'forbidden' ? 'broker_auth' : 'broker_error';
|
|
293
|
+
throw new DeployError(reason, `Failed to acquire deploy lock (${res.status}): ${text.slice(0, 512)}`);
|
|
267
294
|
}
|
|
268
295
|
info(`deploy lock acquired for ${appName}`);
|
|
269
296
|
}
|
|
270
297
|
// best-effort — the 2-min TTL backstop handles failures
|
|
271
298
|
async function releaseLockBestEffort(appName, token) {
|
|
272
299
|
try {
|
|
273
|
-
const res = await
|
|
274
|
-
method: 'POST',
|
|
275
|
-
headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json', ...routingHeaders() },
|
|
276
|
-
body: JSON.stringify({ appName }),
|
|
277
|
-
});
|
|
300
|
+
const res = await postToLockEndpoint('/z2h-cli/lock/release', appName, token);
|
|
278
301
|
if (!res.ok) {
|
|
279
302
|
warn(`failed to release lock for ${appName} (${res.status})`);
|
|
280
303
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"grant.d.ts","sourceRoot":"","sources":["../../../src/commands/grant.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"grant.d.ts","sourceRoot":"","sources":["../../../src/commands/grant.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC;CACvC;AAkBD,wBAAsB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA4B/F;AAED,wBAAsB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBhG;AAED,wBAAsB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAmBnF"}
|
|
@@ -2,6 +2,7 @@ import { output } from '../util/logger.mjs';
|
|
|
2
2
|
import { mfAppName, getCwdAppName } from '../util/app-name.mjs';
|
|
3
3
|
import { brokerBaseUrl, routingHeaders } from '../constants.mjs';
|
|
4
4
|
import { readZthAuth } from '../util/auth/auth-store.mjs';
|
|
5
|
+
import { GrantError } from '../util/errors.mjs';
|
|
5
6
|
|
|
6
7
|
async function postGrant(appName, token, body) {
|
|
7
8
|
const res = await fetch(`${brokerBaseUrl()}/z2h-apps/${appName}/grants`, {
|
|
@@ -11,16 +12,16 @@ async function postGrant(appName, token, body) {
|
|
|
11
12
|
});
|
|
12
13
|
if (!res.ok) {
|
|
13
14
|
const text = await res.text();
|
|
14
|
-
throw new
|
|
15
|
+
throw new GrantError(res.status, `Grant failed (${res.status}): ${text.slice(0, 1024)}`);
|
|
15
16
|
}
|
|
16
17
|
}
|
|
17
18
|
async function grantCommand(email, opts) {
|
|
18
19
|
if (!opts.public && !email) {
|
|
19
|
-
throw new
|
|
20
|
+
throw new GrantError('validation', 'Specify what to grant — e.g. --public or provide an email');
|
|
20
21
|
}
|
|
21
22
|
const level = opts.level ?? 'viewer';
|
|
22
23
|
if (!['owner', 'editor', 'viewer'].includes(level)) {
|
|
23
|
-
throw new
|
|
24
|
+
throw new GrantError('validation', `Invalid --level "${level}". Must be owner, editor, or viewer`);
|
|
24
25
|
}
|
|
25
26
|
const appName = mfAppName(getCwdAppName());
|
|
26
27
|
const { token } = await readZthAuth();
|
|
@@ -41,7 +42,7 @@ async function grantCommand(email, opts) {
|
|
|
41
42
|
}
|
|
42
43
|
async function revokeCommand(email, opts) {
|
|
43
44
|
if (!opts.public && !email) {
|
|
44
|
-
throw new
|
|
45
|
+
throw new GrantError('validation', 'Specify what to revoke — e.g. --public or provide an email');
|
|
45
46
|
}
|
|
46
47
|
const appName = mfAppName(getCwdAppName());
|
|
47
48
|
const { token } = await readZthAuth();
|
|
@@ -52,7 +53,7 @@ async function revokeCommand(email, opts) {
|
|
|
52
53
|
});
|
|
53
54
|
if (!res.ok) {
|
|
54
55
|
const text = await res.text();
|
|
55
|
-
throw new
|
|
56
|
+
throw new GrantError(res.status, `Revoke failed (${res.status}): ${text.slice(0, 1024)}`);
|
|
56
57
|
}
|
|
57
58
|
const revoked = email ?? 'public';
|
|
58
59
|
const msg = email
|
|
@@ -62,7 +63,7 @@ async function revokeCommand(email, opts) {
|
|
|
62
63
|
}
|
|
63
64
|
async function transferOwnerCommand(email) {
|
|
64
65
|
if (!email) {
|
|
65
|
-
throw new
|
|
66
|
+
throw new GrantError('validation', 'Specify the new owner — e.g. z2h-cli transfer-owner someone@monday.com');
|
|
66
67
|
}
|
|
67
68
|
const appName = mfAppName(getCwdAppName());
|
|
68
69
|
const { token } = await readZthAuth();
|
|
@@ -73,7 +74,7 @@ async function transferOwnerCommand(email) {
|
|
|
73
74
|
});
|
|
74
75
|
if (!res.ok) {
|
|
75
76
|
const text = await res.text();
|
|
76
|
-
throw new
|
|
77
|
+
throw new GrantError(res.status, `Transfer owner failed (${res.status}): ${text.slice(0, 1024)}`);
|
|
77
78
|
}
|
|
78
79
|
output(`[z2h-cli] "${appName}" ownership transferred to ${email}`, { ok: true, appName, newOwner: email });
|
|
79
80
|
}
|