@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
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Base for every error the CLI classifies. runCommand in ./run-command.ts reads `reason`
|
|
5
|
+
* and `data` straight off the error, so the throw site is the only place that needs to know
|
|
6
|
+
* why it failed. Nothing re-derives it from the error type at the top level.
|
|
7
|
+
*
|
|
8
|
+
* `exitCode` lets a throw site override the CLI's --help contract, 1 for user-resolvable
|
|
9
|
+
* and 2 for system failures, per error instead of per command. Nothing sets it yet. The
|
|
10
|
+
* command table still pins exit codes, which keeps today's behavior.
|
|
11
|
+
*/
|
|
12
|
+
class Z2hError extends Error {
|
|
13
|
+
reason;
|
|
14
|
+
data;
|
|
15
|
+
exitCode;
|
|
16
|
+
constructor(message, reason, data, exitCode = 1) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.reason = reason;
|
|
19
|
+
this.data = data;
|
|
20
|
+
this.exitCode = exitCode;
|
|
21
|
+
this.name = new.target.name;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/** Tags a deploy/preview stage failure with a bounded reason code instead of a freeform message. */
|
|
25
|
+
class DeployError extends Z2hError {
|
|
26
|
+
reason;
|
|
27
|
+
constructor(reason, message, data) {
|
|
28
|
+
super(message, reason, data);
|
|
29
|
+
this.reason = reason;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Tags a grant/revoke/transfer-owner failure with a bounded reason code instead of a freeform message.
|
|
34
|
+
* `reason` also accepts a raw HTTP status, which gets mapped to a bounded reason internally.
|
|
35
|
+
*/
|
|
36
|
+
class GrantError extends Z2hError {
|
|
37
|
+
constructor(reason, message, data) {
|
|
38
|
+
super(message, typeof reason === 'number' ? grantReasonForStatus(reason) : reason, data);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/** No z2h JWT found — see auth-store.ts for the discovery order (env var, then ~/.zth/auth.json). */
|
|
42
|
+
class Z2hAuthMissingError extends Z2hError {
|
|
43
|
+
authFilePath;
|
|
44
|
+
constructor(authFilePath) {
|
|
45
|
+
super(`No Z2H deploy token found at ${authFilePath}. Please run 'z2h-cli generate-z2h-token' command.`, 'read_auth_token');
|
|
46
|
+
this.authFilePath = authFilePath;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/** A git pull diverged or a push was rejected; `reason` distinguishes which stage hit it. */
|
|
50
|
+
class GitConflictError extends Z2hError {
|
|
51
|
+
constructor(message, reason = 'git_error') {
|
|
52
|
+
super(message, reason);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/** Maps an HTTP status to a bounded grant/revoke/transfer-owner failure reason. */
|
|
56
|
+
function grantReasonForStatus(status) {
|
|
57
|
+
// 401 is an expired/invalid token (re-auth fixes it); 403 is a real permission denial.
|
|
58
|
+
if (status === 401) {
|
|
59
|
+
return 'auth';
|
|
60
|
+
}
|
|
61
|
+
if (status === 403) {
|
|
62
|
+
return 'forbidden';
|
|
63
|
+
}
|
|
64
|
+
if (status === 404) {
|
|
65
|
+
return 'not_found';
|
|
66
|
+
}
|
|
67
|
+
if (status >= 500) {
|
|
68
|
+
return 'server_error';
|
|
69
|
+
}
|
|
70
|
+
return 'unknown';
|
|
71
|
+
}
|
|
72
|
+
/** Tags a failure with `reason`, unless `err` is already classified; `signal` overrides `reason` for a killed child process. */
|
|
73
|
+
function tagDeployError(reason, err) {
|
|
74
|
+
if (err instanceof Z2hError) {
|
|
75
|
+
return err;
|
|
76
|
+
}
|
|
77
|
+
const signal = err instanceof Error ? err.signal : undefined;
|
|
78
|
+
return new DeployError(signal ? 'interrupted' : reason, messageOf(err));
|
|
79
|
+
}
|
|
80
|
+
/** Reads the bounded reason off a classified error; anything unclassified is `unknown`. */
|
|
81
|
+
function reasonOf(err) {
|
|
82
|
+
return err instanceof Z2hError ? err.reason : 'unknown';
|
|
83
|
+
}
|
|
84
|
+
/** Extra telemetry dimensions the throw site attached, if any. */
|
|
85
|
+
function dataOf(err) {
|
|
86
|
+
return err instanceof Z2hError ? err.data ?? {} : {};
|
|
87
|
+
}
|
|
88
|
+
/** Safe message for any thrown value. A non-Error throw must not break the error path. */
|
|
89
|
+
function messageOf(err) {
|
|
90
|
+
return err instanceof Error ? err.message : String(err);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
exports.DeployError = DeployError;
|
|
94
|
+
exports.GitConflictError = GitConflictError;
|
|
95
|
+
exports.GrantError = GrantError;
|
|
96
|
+
exports.Z2hAuthMissingError = Z2hAuthMissingError;
|
|
97
|
+
exports.Z2hError = Z2hError;
|
|
98
|
+
exports.dataOf = dataOf;
|
|
99
|
+
exports.grantReasonForStatus = grantReasonForStatus;
|
|
100
|
+
exports.messageOf = messageOf;
|
|
101
|
+
exports.reasonOf = reasonOf;
|
|
102
|
+
exports.tagDeployError = tagDeployError;
|
package/dist/util/git/repo.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repo.d.ts","sourceRoot":"","sources":["../../../src/util/git/repo.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"repo.d.ts","sourceRoot":"","sources":["../../../src/util/git/repo.ts"],"names":[],"mappings":"AAmBA,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,gBAAgB,EAAE,MAAM,EAAE,CAAC;CAC5B;AAKD,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,wBAAsB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAOzE;AAED,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAI3E;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAK/D;AAED,wBAAsB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAG5G;AAED,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAclE;AAED,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CA2B7F;AAED;;;;;;GAMG;AACH,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAqB3F;AAED,wBAAsB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAO7D;AAED,wBAAsB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAG5D"}
|
package/dist/util/git/repo.js
CHANGED
|
@@ -5,6 +5,7 @@ const fs = require('fs-extra');
|
|
|
5
5
|
const execa = require('execa');
|
|
6
6
|
const constants = require('../../constants.js');
|
|
7
7
|
const util_appName = require('../app-name.js');
|
|
8
|
+
const util_errors = require('../errors.js');
|
|
8
9
|
|
|
9
10
|
const _interopDefault = e => e && e.__esModule ? e : { default: e };
|
|
10
11
|
|
|
@@ -19,12 +20,6 @@ async function execGitLines(args, dir, env) {
|
|
|
19
20
|
.then(r => r.stdout.split('\n').filter(Boolean))
|
|
20
21
|
.catch(() => []);
|
|
21
22
|
}
|
|
22
|
-
class GitConflictError extends Error {
|
|
23
|
-
constructor(message) {
|
|
24
|
-
super(message);
|
|
25
|
-
this.name = 'GitConflictError';
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
23
|
const REQUIRED_GITIGNORE_ENTRIES = ['node_modules/', '.zth/', 'build/'];
|
|
29
24
|
const LOCAL_HOOKS_DIR = '.zth/hooks';
|
|
30
25
|
function defaultS3Remote(app) {
|
|
@@ -98,7 +93,7 @@ async function pullAndResolve(dir, env) {
|
|
|
98
93
|
}
|
|
99
94
|
await safeBackupIfDirty(dir);
|
|
100
95
|
const info = await gatherConflictInfo(dir, gitEnv);
|
|
101
|
-
throw new GitConflictError(`Pull rejected: Git conflict detected: ${JSON.stringify(info)}`);
|
|
96
|
+
throw new util_errors.GitConflictError(`Pull rejected: Git conflict detected: ${JSON.stringify(info)}`);
|
|
102
97
|
}
|
|
103
98
|
}
|
|
104
99
|
/**
|
|
@@ -126,9 +121,9 @@ async function pushToRemote(dir, env) {
|
|
|
126
121
|
}
|
|
127
122
|
if (isRejected) {
|
|
128
123
|
const info = await gatherConflictInfo(dir, gitEnv);
|
|
129
|
-
throw new GitConflictError(`Push rejected: Git conflict detected: ${JSON.stringify(info)}
|
|
124
|
+
throw new util_errors.GitConflictError(`Push rejected: Git conflict detected: ${JSON.stringify(info)}`, 'push_error');
|
|
130
125
|
}
|
|
131
|
-
throw err;
|
|
126
|
+
throw new util_errors.DeployError('push_error', util_errors.messageOf(err));
|
|
132
127
|
}
|
|
133
128
|
}
|
|
134
129
|
async function remoteHead(dir) {
|
|
@@ -183,7 +178,6 @@ async function ensureGitignore(dir) {
|
|
|
183
178
|
await fs__default.default.outputFile(gitignorePath, content + separator + missing.join('\n') + '\n');
|
|
184
179
|
}
|
|
185
180
|
|
|
186
|
-
exports.GitConflictError = GitConflictError;
|
|
187
181
|
exports.cloneRepo = cloneRepo;
|
|
188
182
|
exports.defaultS3Remote = defaultS3Remote;
|
|
189
183
|
exports.getRemoteUrl = getRemoteUrl;
|
package/dist/util/logger.d.ts
CHANGED
|
@@ -17,6 +17,9 @@ export declare function output(human: string, data: unknown): void;
|
|
|
17
17
|
/**
|
|
18
18
|
* Emit a structured failure payload (JSON mode) or a red error line (human mode),
|
|
19
19
|
* then exit with the given code.
|
|
20
|
+
*
|
|
21
|
+
* `track: false` skips the telemetry event but still prints and exits. Commands with no
|
|
22
|
+
* success event pass it, so their failures don't skew per-command error rates.
|
|
20
23
|
*/
|
|
21
|
-
export declare function fail(message: string, payload: Record<string, unknown>, exitCode?: number): Promise<never>;
|
|
24
|
+
export declare function fail(message: string, payload: Record<string, unknown>, exitCode?: number, track?: boolean): Promise<never>;
|
|
22
25
|
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/util/logger.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/util/logger.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,MAAM,CAAC;AAI1C,wBAAgB,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,CAEpD;AAED,wBAAgB,aAAa,IAAI,UAAU,CAE1C;AAED;;;;GAIG;AACH,wBAAgB,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAItC;AAED,wBAAgB,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAItC;AAED,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAI1C;AAED;;;GAGG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAMzD;AAED;;;;;;GAMG;AACH,wBAAsB,IAAI,CACxB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,QAAQ,SAAI,EACZ,KAAK,UAAO,GACX,OAAO,CAAC,KAAK,CAAC,CAehB"}
|
package/dist/util/logger.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
|
|
3
3
|
const pc = require('picocolors');
|
|
4
|
+
const observability = require('@mondaydotcomorg/z2h-shared-utils/observability');
|
|
4
5
|
const util_tracker = require('./tracker.js');
|
|
5
6
|
|
|
6
7
|
const _interopDefault = e => e && e.__esModule ? e : { default: e };
|
|
@@ -49,10 +50,15 @@ function output(human, data) {
|
|
|
49
50
|
/**
|
|
50
51
|
* Emit a structured failure payload (JSON mode) or a red error line (human mode),
|
|
51
52
|
* then exit with the given code.
|
|
53
|
+
*
|
|
54
|
+
* `track: false` skips the telemetry event but still prints and exits. Commands with no
|
|
55
|
+
* success event pass it, so their failures don't skew per-command error rates.
|
|
52
56
|
*/
|
|
53
|
-
async function fail(message, payload, exitCode = 1) {
|
|
57
|
+
async function fail(message, payload, exitCode = 1, track = true) {
|
|
58
|
+
// Canonical fields go last so a payload key can never shadow `error`/`action`.
|
|
59
|
+
const { command, ...rest } = payload;
|
|
54
60
|
if (mode === 'json') {
|
|
55
|
-
process.stdout.write(`${JSON.stringify({ ok: false, error: message
|
|
61
|
+
process.stdout.write(`${JSON.stringify({ ok: false, ...payload, error: message })}\n`);
|
|
56
62
|
}
|
|
57
63
|
else {
|
|
58
64
|
process.stderr.write(pc__default.default.red(`[z2h-cli] ${message}\n`));
|
|
@@ -60,7 +66,9 @@ async function fail(message, payload, exitCode = 1) {
|
|
|
60
66
|
process.stderr.write(pc__default.default.dim(` → ${payload.suggestedCommand}\n`));
|
|
61
67
|
}
|
|
62
68
|
}
|
|
63
|
-
|
|
69
|
+
if (track) {
|
|
70
|
+
await util_tracker.trackEvent(observability.Z2hCliEvents.error, { ...rest, action: command ?? 'unknown', error: message });
|
|
71
|
+
}
|
|
64
72
|
process.exit(exitCode);
|
|
65
73
|
}
|
|
66
74
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
import type { CommandSpec } from '../types';
|
|
3
|
+
/**
|
|
4
|
+
* Wraps a command body with the whole telemetry and failure contract so no command
|
|
5
|
+
* hand-rolls it. It times the happy path. On a throw it reads `reason` and `data` straight
|
|
6
|
+
* off the error, defined in ./errors, instead of re-deriving them per command.
|
|
7
|
+
*
|
|
8
|
+
* Returns a commander action handler, chainable straight off `.command(...)` — commander always
|
|
9
|
+
* appends the Command instance as the action handler's last argument, so this splits it back off
|
|
10
|
+
* to read the command's own name/parent instead of asking the caller to pass it in.
|
|
11
|
+
*
|
|
12
|
+
* NoInfer keeps the arg types coming from `fn` alone. A `data` or `label` callback that
|
|
13
|
+
* only needs the first arg must not narrow the handler's own signature.
|
|
14
|
+
*/
|
|
15
|
+
export declare function runCommand<A extends unknown[]>(spec: CommandSpec<NoInfer<A>>, fn: (...args: A) => Promise<unknown>): (...args: [...A, Command]) => Promise<void>;
|
|
16
|
+
//# sourceMappingURL=run-command.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-command.d.ts","sourceRoot":"","sources":["../../src/util/run-command.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAW,MAAM,UAAU,CAAC;AAcrD;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,OAAO,EAAE,EAC5C,IAAI,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAC7B,EAAE,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,GACnC,CAAC,GAAG,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAyB7C"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
|
|
3
|
+
const util_logger = require('./logger.js');
|
|
4
|
+
const util_tracker = require('./tracker.js');
|
|
5
|
+
const util_errors = require('./errors.js');
|
|
6
|
+
|
|
7
|
+
/** A spec field is a constant, a callback over the args, or absent — in which case `fallback` applies. */
|
|
8
|
+
const resolveSpecField = (field, args, fallback) => {
|
|
9
|
+
if (field === undefined) {
|
|
10
|
+
return fallback;
|
|
11
|
+
}
|
|
12
|
+
return typeof field === 'function' ? field(args) : field;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Wraps a command body with the whole telemetry and failure contract so no command
|
|
16
|
+
* hand-rolls it. It times the happy path. On a throw it reads `reason` and `data` straight
|
|
17
|
+
* off the error, defined in ./errors, instead of re-deriving them per command.
|
|
18
|
+
*
|
|
19
|
+
* Returns a commander action handler, chainable straight off `.command(...)` — commander always
|
|
20
|
+
* appends the Command instance as the action handler's last argument, so this splits it back off
|
|
21
|
+
* to read the command's own name/parent instead of asking the caller to pass it in.
|
|
22
|
+
*
|
|
23
|
+
* NoInfer keeps the arg types coming from `fn` alone. A `data` or `label` callback that
|
|
24
|
+
* only needs the first arg must not narrow the handler's own signature.
|
|
25
|
+
*/
|
|
26
|
+
function runCommand(spec, fn) {
|
|
27
|
+
return async (...allArgs) => {
|
|
28
|
+
const cmd = allArgs[allArgs.length - 1];
|
|
29
|
+
const args = allArgs.slice(0, -1);
|
|
30
|
+
const name = cmd.parent ? `${cmd.parent.name()} ${cmd.name()}` : cmd.name();
|
|
31
|
+
const resolve = (v, fallback) => resolveSpecField(v, args, fallback);
|
|
32
|
+
const track = resolve(spec.track, true);
|
|
33
|
+
const label = resolve(spec.label, name);
|
|
34
|
+
const event = resolve(spec.event, util_tracker.successEvent(name));
|
|
35
|
+
const data = spec.data?.(args) ?? {};
|
|
36
|
+
try {
|
|
37
|
+
await (track ? util_tracker.trackDuration(event, () => fn(...args), data) : fn(...args));
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
const reason = util_errors.reasonOf(err);
|
|
41
|
+
// A deploy/preview that failed because the consumer's own build broke isn't a CLI/infra
|
|
42
|
+
// problem — don't report it at all, not even as `unknown`.
|
|
43
|
+
const shouldTrack = track && reason !== 'build_error';
|
|
44
|
+
await util_logger.fail(`${label} failed: ${util_errors.messageOf(err)}`, { command: label, reason, ...data, ...util_errors.dataOf(err) }, spec.exitCode ?? (err instanceof util_errors.Z2hError ? err.exitCode : 1), shouldTrack);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
exports.runCommand = runCommand;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../../../src/util/s3/upload.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../../../src/util/s3/upload.ts"],"names":[],"mappings":"AAgBA,wBAAsB,SAAS,CAC7B,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,MAAM,GAAE,MAA0B,GACjC,OAAO,CAAC,MAAM,EAAE,CAAC,CA8BnB"}
|
package/dist/util/s3/upload.js
CHANGED
|
@@ -7,6 +7,7 @@ const mimeTypes = require('mime-types');
|
|
|
7
7
|
const pMap = require('p-map');
|
|
8
8
|
const clientS3 = require('@aws-sdk/client-s3');
|
|
9
9
|
const constants = require('../../constants.js');
|
|
10
|
+
const util_errors = require('../errors.js');
|
|
10
11
|
const util_s3_client = require('./client.js');
|
|
11
12
|
|
|
12
13
|
const _interopDefault = e => e && e.__esModule ? e : { default: e };
|
|
@@ -19,22 +20,27 @@ const pMap__default = /*#__PURE__*/_interopDefault(pMap);
|
|
|
19
20
|
const FOREVER = 'public, immutable';
|
|
20
21
|
const UPLOAD_CONCURRENCY = 5;
|
|
21
22
|
async function uploadDir(localDir, keyPrefix, bucket = constants.BUCKET_WRITE_NAME) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
23
|
+
try {
|
|
24
|
+
const entries = await promises.readdir(localDir, { recursive: true, withFileTypes: true });
|
|
25
|
+
const files = entries.filter(e => e.isFile()).map(e => path__default.default.relative(localDir, path__default.default.join(e.parentPath, e.name)));
|
|
26
|
+
const s3 = util_s3_client.getS3Client();
|
|
27
|
+
await pMap__default.default(files, async (rel) => {
|
|
28
|
+
const body = await fs.readFile(path__default.default.join(localDir, rel));
|
|
29
|
+
const contentType = mimeTypes.lookup(rel) || 'application/octet-stream';
|
|
30
|
+
const key = `${keyPrefix}/${rel.split(path__default.default.sep).join('/')}`;
|
|
31
|
+
await s3.send(new clientS3.PutObjectCommand({
|
|
32
|
+
Bucket: bucket,
|
|
33
|
+
Key: key,
|
|
34
|
+
Body: body,
|
|
35
|
+
ContentType: contentType,
|
|
36
|
+
CacheControl: FOREVER,
|
|
37
|
+
}));
|
|
38
|
+
}, { concurrency: UPLOAD_CONCURRENCY });
|
|
39
|
+
return files.map(f => f.split(path__default.default.sep).join('/'));
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
throw util_errors.tagDeployError('s3_error', err);
|
|
43
|
+
}
|
|
38
44
|
}
|
|
39
45
|
|
|
40
46
|
exports.uploadDir = uploadDir;
|
package/dist/util/tracker.d.ts
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
-
export declare
|
|
2
|
-
|
|
1
|
+
export declare const trackEvent: (name: string, data: Record<string, unknown>) => Promise<void>, trackDuration: <T>(name: string, fn: () => Promise<T>, data?: Record<string, unknown>) => Promise<T>;
|
|
2
|
+
/**
|
|
3
|
+
* `z2h_cli_<command>_success` — the success-event name for every command, derived by
|
|
4
|
+
* util/run-command.ts's `runCommand()` so no command has to choose one. Nested commands join
|
|
5
|
+
* their path with `_` (`backend invoke` → `z2h_cli_backend_invoke_success`). The one place a
|
|
6
|
+
* command names its own event, `deploy --preview`, still goes through this function so the
|
|
7
|
+
* convention holds everywhere. See CommandSpec's `event` field in types.ts.
|
|
8
|
+
*/
|
|
9
|
+
export declare function successEvent(command: string): string;
|
|
3
10
|
//# sourceMappingURL=tracker.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tracker.d.ts","sourceRoot":"","sources":["../../src/util/tracker.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"tracker.d.ts","sourceRoot":"","sources":["../../src/util/tracker.ts"],"names":[],"mappings":"AAcA,eAAO,MAAQ,UAAU,kEAAE,aAAa,uFAItC,CAAC;AAEH;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEpD"}
|
package/dist/util/tracker.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
|
|
3
3
|
const os = require('node:os');
|
|
4
|
+
const node_module = require('node:module');
|
|
5
|
+
const observability = require('@mondaydotcomorg/z2h-shared-utils/observability');
|
|
4
6
|
|
|
7
|
+
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
5
8
|
const _interopDefault = e => e && e.__esModule ? e : { default: e };
|
|
6
9
|
|
|
7
10
|
const os__default = /*#__PURE__*/_interopDefault(os);
|
|
@@ -9,22 +12,24 @@ const os__default = /*#__PURE__*/_interopDefault(os);
|
|
|
9
12
|
const DOORMAN_URL = 'https://track-int.bigbrain.me/prod/event';
|
|
10
13
|
const DOORMAN_URL_STAGING = 'https://track-int.bigbrainstaging.me/stage/event';
|
|
11
14
|
const username = os__default.default.userInfo().username;
|
|
15
|
+
const { version: cliVersion } = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('util/tracker.js', document.baseURI).href)))('@mondaydotcomorg/z2h-cli/package.json');
|
|
12
16
|
const isStaging = process.env.NODE_ENV === 'staging';
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
return
|
|
17
|
+
const { trackEvent, trackDuration } = observability.createHttpTracker({
|
|
18
|
+
url: isStaging ? DOORMAN_URL_STAGING : DOORMAN_URL,
|
|
19
|
+
source: 'z2h-cli',
|
|
20
|
+
getContext: () => ({ user_name: username, cli_version: cliVersion }),
|
|
21
|
+
});
|
|
22
|
+
/**
|
|
23
|
+
* `z2h_cli_<command>_success` — the success-event name for every command, derived by
|
|
24
|
+
* util/run-command.ts's `runCommand()` so no command has to choose one. Nested commands join
|
|
25
|
+
* their path with `_` (`backend invoke` → `z2h_cli_backend_invoke_success`). The one place a
|
|
26
|
+
* command names its own event, `deploy --preview`, still goes through this function so the
|
|
27
|
+
* convention holds everywhere. See CommandSpec's `event` field in types.ts.
|
|
28
|
+
*/
|
|
29
|
+
function successEvent(command) {
|
|
30
|
+
return `z2h_cli_${command.replace(/[\s-]+/g, '_')}_success`;
|
|
27
31
|
}
|
|
28
32
|
|
|
33
|
+
exports.successEvent = successEvent;
|
|
29
34
|
exports.trackDuration = trackDuration;
|
|
30
35
|
exports.trackEvent = trackEvent;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { logger } from '@mondaydotcomorg/trident-backend-runtime';
|
|
2
2
|
import { createMondayModel } from '@mondaydotcomorg/kong-ai-gateway-sdk/langchain';
|
|
3
|
-
import { BackendRunnerEvents
|
|
3
|
+
import { BackendRunnerEvents } from '@mondaydotcomorg/z2h-shared-utils/observability';
|
|
4
|
+
import { trackEvent } from '@mondaydotcomorg/z2h-shared-utils/observability/trident';
|
|
4
5
|
|
|
5
6
|
import { ChannelError } from '../../backend-runner.errors';
|
|
6
7
|
import { BI_SOURCE } from '../../constants';
|
|
@@ -209,7 +210,7 @@ export function makeLlmChannel(appName: string, caller: Caller) {
|
|
|
209
210
|
LLM_TIMEOUT_MS,
|
|
210
211
|
'LLM gateway',
|
|
211
212
|
);
|
|
212
|
-
trackEvent(BackendRunnerEvents.llm.call, { appName, email: caller.email, modelId: model }, BI_SOURCE);
|
|
213
|
+
void trackEvent(BackendRunnerEvents.llm.call, { appName, email: caller.email, modelId: model }, BI_SOURCE);
|
|
213
214
|
return { content: extractText(response.content) };
|
|
214
215
|
} catch (err) {
|
|
215
216
|
throw toLlmChannelError(err);
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
|
|
3
3
|
import { context, logger } from '@mondaydotcomorg/trident-backend-runtime';
|
|
4
|
-
import { BackendRunnerEvents
|
|
4
|
+
import { BackendRunnerEvents } from '@mondaydotcomorg/z2h-shared-utils/observability';
|
|
5
|
+
import { trackEvent } from '@mondaydotcomorg/z2h-shared-utils/observability/trident';
|
|
5
6
|
import { getDefaultClient } from 'src/utils/elasticache';
|
|
6
7
|
|
|
7
8
|
import { ChannelError } from '../../backend-runner.errors';
|
|
@@ -109,8 +110,8 @@ function resolveTimeout(timeoutMs: number | undefined): number {
|
|
|
109
110
|
*/
|
|
110
111
|
export async function runSnowflakeQuery(
|
|
111
112
|
sql: string,
|
|
113
|
+
appName: string,
|
|
112
114
|
params?: Record<string, SnowflakeParamValue> | null,
|
|
113
|
-
appName?: string,
|
|
114
115
|
options?: RunQueryOptions | null,
|
|
115
116
|
): Promise<SnowflakeRow[]> {
|
|
116
117
|
const timeoutInMs = resolveTimeout(options?.timeoutMs);
|
|
@@ -138,7 +139,7 @@ export async function runSnowflakeQuery(
|
|
|
138
139
|
const cached = scope ? await readCache(queryCacheKey, scope) : null;
|
|
139
140
|
if (cached) {
|
|
140
141
|
const duration_ms = Date.now() - startMs;
|
|
141
|
-
trackEvent(BackendRunnerEvents.snowflake.queryExecuted, { appName, duration_ms, cache_hit: true }, BI_SOURCE);
|
|
142
|
+
void trackEvent(BackendRunnerEvents.snowflake.queryExecuted, { appName, duration_ms, cache_hit: true }, BI_SOURCE);
|
|
142
143
|
logger.info(
|
|
143
144
|
{
|
|
144
145
|
tag: LOG_TAG,
|
|
@@ -155,7 +156,7 @@ export async function runSnowflakeQuery(
|
|
|
155
156
|
|
|
156
157
|
const queryOptions = {
|
|
157
158
|
query: boundSql,
|
|
158
|
-
identifier: `z2h-query:${queryIdentifier}`,
|
|
159
|
+
identifier: `z2h-query:${appName}:${queryIdentifier}`,
|
|
159
160
|
fallback: [] as SnowflakeRow[],
|
|
160
161
|
timeoutInMs,
|
|
161
162
|
convertToSingularResult: false,
|
|
@@ -182,7 +183,7 @@ export async function runSnowflakeQuery(
|
|
|
182
183
|
);
|
|
183
184
|
} catch (err) {
|
|
184
185
|
// Failure BI tracking already happens generically at the invoke level
|
|
185
|
-
// (runner.service.ts's
|
|
186
|
+
// (runner.service.ts's BackendRunnerEvents.invoke.failed, tagged with this
|
|
186
187
|
// ChannelError's `code`) — this stays a log only, for the queryHash/
|
|
187
188
|
// queryIdentifier that generic event can't know about.
|
|
188
189
|
const duration_ms = Date.now() - startMs;
|
|
@@ -195,7 +196,7 @@ export async function runSnowflakeQuery(
|
|
|
195
196
|
await writeCache(queryCacheKey, scope, rows);
|
|
196
197
|
}
|
|
197
198
|
const duration_ms = Date.now() - startMs;
|
|
198
|
-
trackEvent(BackendRunnerEvents.snowflake.queryExecuted, { appName, duration_ms, cache_hit: false }, BI_SOURCE);
|
|
199
|
+
void trackEvent(BackendRunnerEvents.snowflake.queryExecuted, { appName, duration_ms, cache_hit: false }, BI_SOURCE);
|
|
199
200
|
logger.info(
|
|
200
201
|
{ tag: LOG_TAG, queryHash: queryCacheKey, queryIdentifier, duration_ms, rowCount: rows.length, cache_hit: false },
|
|
201
202
|
'query executed',
|
|
@@ -96,8 +96,8 @@ export function makeSnowflakeChannel(appName: string) {
|
|
|
96
96
|
}
|
|
97
97
|
return runSnowflakeQuery(
|
|
98
98
|
query,
|
|
99
|
-
params as Record<string, SnowflakeParamValue> | null | undefined,
|
|
100
99
|
appName,
|
|
100
|
+
params as Record<string, SnowflakeParamValue> | null | undefined,
|
|
101
101
|
options as RunQueryOptions | null | undefined,
|
|
102
102
|
);
|
|
103
103
|
},
|
|
@@ -23,10 +23,7 @@ export interface SnowflakeConnectionDetails {
|
|
|
23
23
|
defaultDatabase?: string;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
export function isStatelessPartBApp(appName: string
|
|
27
|
-
if (!appName) {
|
|
28
|
-
return false;
|
|
29
|
-
}
|
|
26
|
+
export function isStatelessPartBApp(appName: string): boolean {
|
|
30
27
|
const { allowed } = getConfiguration(ConfigurationKey.Z2H_STATELESS_PART_B_APPS);
|
|
31
28
|
return Array.isArray(allowed) && allowed.includes(appName);
|
|
32
29
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mondaydotcomorg/z2h-cli",
|
|
3
|
-
"version": "0.25.
|
|
3
|
+
"version": "0.25.7",
|
|
4
4
|
"bin": "./bin/z2h-cli.js",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
],
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"@aws-sdk/client-s3": "^3.706.0",
|
|
54
|
-
"@mondaydotcomorg/z2h-shared-utils": "^0.
|
|
54
|
+
"@mondaydotcomorg/z2h-shared-utils": "^0.4.1",
|
|
55
55
|
"chokidar": "^3.6.0",
|
|
56
56
|
"commander": "^12.1.0",
|
|
57
57
|
"cors": "^2.8.6",
|
package/src/auth.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
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';
|
|
@@ -5,6 +5,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
|
5
5
|
|
|
6
6
|
import { deployBackend, hasHandlers } from '../backend';
|
|
7
7
|
import { readHandlers } from '../../backend/handlers';
|
|
8
|
+
import { DeployError } from '../../util/errors';
|
|
8
9
|
|
|
9
10
|
vi.mock('../../backend/handlers', () => ({
|
|
10
11
|
readHandlers: vi.fn(),
|
|
@@ -96,6 +97,23 @@ describe('deployBackend (live)', () => {
|
|
|
96
97
|
const keys: string[] = mockSend.mock.calls.map((c: unknown[]) => (c[0] as { input: { Key: string } }).input.Key);
|
|
97
98
|
expect(keys.some(k => k.includes('/backend-1/'))).toBe(true);
|
|
98
99
|
});
|
|
100
|
+
|
|
101
|
+
it('tags a handler upload failure as deploy_backend_error, not register_error', async () => {
|
|
102
|
+
await ensureDir(path.join(tmp, 'backend', 'handlers'));
|
|
103
|
+
mockSend.mockRejectedValueOnce(new Error('AccessDenied'));
|
|
104
|
+
|
|
105
|
+
const err = await deployBackend({
|
|
106
|
+
appDir: tmp,
|
|
107
|
+
appName: 'mf-my-app',
|
|
108
|
+
mfKeyPrefix: 'mf-my-app/1',
|
|
109
|
+
version: 1,
|
|
110
|
+
deployedBy: 'test-user',
|
|
111
|
+
isPreview: false,
|
|
112
|
+
}).catch((e: unknown) => e);
|
|
113
|
+
expect(err).toBeInstanceOf(DeployError);
|
|
114
|
+
expect((err as DeployError).reason).toBe('deploy_backend_error');
|
|
115
|
+
expect((err as DeployError).message).toBe('AccessDenied');
|
|
116
|
+
});
|
|
99
117
|
});
|
|
100
118
|
|
|
101
119
|
describe('deployBackend (preview)', () => {
|