@scalequality/cli 0.4.10 → 0.4.12
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/connect.build.json +2 -2
- package/dist/connect.cjs +103 -38
- package/package.json +1 -1
package/dist/connect.build.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
2
|
"sdkVersion": "0.3.281",
|
|
3
|
-
"sha256": "
|
|
4
|
-
"sourceCommit": "
|
|
3
|
+
"sha256": "7b199d28673b767dce41406f737d3c8042ed0658ca1a233a7d8363b5b7b27dfd",
|
|
4
|
+
"sourceCommit": "0da92af88f77faa5f860d7f37480a65800017946"
|
|
5
5
|
}
|
package/dist/connect.cjs
CHANGED
|
@@ -1290,13 +1290,14 @@ async function decideToolUse(toolName, input, ctx) {
|
|
|
1290
1290
|
}
|
|
1291
1291
|
|
|
1292
1292
|
// src/application/services/workspaceSandbox/protections.ts
|
|
1293
|
-
var NO_PROTECTIONS = Object.freeze({ secretFiles: false, protectedPush: false, deployApproval: false, destructiveCommands: false, unknownPackage: null });
|
|
1293
|
+
var NO_PROTECTIONS = Object.freeze({ secretFiles: false, protectedPush: false, deployApproval: false, destructiveCommands: false, unknownPackage: null, toolApproval: false });
|
|
1294
1294
|
var KNOWN_RULES = {
|
|
1295
1295
|
"secret-files": ["block"],
|
|
1296
1296
|
"protected-push": ["require"],
|
|
1297
1297
|
"deploy-approval": ["require"],
|
|
1298
1298
|
"destructive-commands": ["require"],
|
|
1299
|
-
"unknown-package": ["block", "warn"]
|
|
1299
|
+
"unknown-package": ["block", "warn"],
|
|
1300
|
+
"tool-approval": ["require"]
|
|
1300
1301
|
};
|
|
1301
1302
|
function protectionsWireOf(raw) {
|
|
1302
1303
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
@@ -1318,7 +1319,8 @@ function parseProtections(raw) {
|
|
|
1318
1319
|
protectedPush: r["protected-push"] === "require",
|
|
1319
1320
|
deployApproval: r["deploy-approval"] === "require",
|
|
1320
1321
|
destructiveCommands: r["destructive-commands"] === "require",
|
|
1321
|
-
unknownPackage: r["unknown-package"] === "block" || r["unknown-package"] === "warn" ? r["unknown-package"] : null
|
|
1322
|
+
unknownPackage: r["unknown-package"] === "block" || r["unknown-package"] === "warn" ? r["unknown-package"] : null,
|
|
1323
|
+
toolApproval: r["tool-approval"] === "require"
|
|
1322
1324
|
};
|
|
1323
1325
|
}
|
|
1324
1326
|
var SECRET_FILE_MESSAGE = "Blocked by your organization's protection: secret files are never sent to the model. Do not try to read this file another way; ask the user for the specific value your task needs, or use the example file (such as .env.example).";
|
|
@@ -1857,6 +1859,7 @@ var RegistryChecker = class {
|
|
|
1857
1859
|
}
|
|
1858
1860
|
};
|
|
1859
1861
|
function mayNeedProtection(command, p) {
|
|
1862
|
+
if (p.toolApproval) return true;
|
|
1860
1863
|
if (p.destructiveCommands && isDestructiveCommand(command)) return true;
|
|
1861
1864
|
if (p.deployApproval && isDeployCommand(command)) return true;
|
|
1862
1865
|
if (p.protectedPush && gitPushes(command).length > 0) return true;
|
|
@@ -1889,6 +1892,7 @@ async function protectionVerdict(command, p, deps) {
|
|
|
1889
1892
|
if (p.destructiveCommands && isDestructiveCommand(command)) reasons.push("PROTECTION_DESTRUCTIVE_COMMAND");
|
|
1890
1893
|
if (p.deployApproval && isDeployCommand(command)) reasons.push("PROTECTION_DEPLOY");
|
|
1891
1894
|
if (p.protectedPush && await isProtectedPush(command, deps.currentBranch)) reasons.push("PROTECTION_PROTECTED_PUSH");
|
|
1895
|
+
if (p.toolApproval && !reasons.length) reasons.push("PROTECTION_TOOL_APPROVAL");
|
|
1892
1896
|
if (!reasons.length) return { kind: "allow" };
|
|
1893
1897
|
return { kind: "ask", reasons, ...pkg ? { package: pkg.name, packageReason: pkg.reason } : {} };
|
|
1894
1898
|
}
|
|
@@ -1896,7 +1900,12 @@ var PROTECTION_MESSAGES = {
|
|
|
1896
1900
|
rejected: "Your organization's protection requires a person to approve this command, and the user denied it. It did not run. Do not run it again or another way unless the user asks.",
|
|
1897
1901
|
timedOut: (minutes) => `Your organization's protection requires a person to approve this command, and nobody answered within ${minutes} minutes. It did not run. Ask the user before trying it again.`,
|
|
1898
1902
|
stopped: "The request was stopped before the command ran.",
|
|
1899
|
-
unavailable: "Your organization's protection requires a person to approve this command, and the approval could not be requested. It did not run. Tell the user; do not try to run it another way."
|
|
1903
|
+
unavailable: "Your organization's protection requires a person to approve this command, and the approval could not be requested. It did not run. Tell the user; do not try to run it another way.",
|
|
1904
|
+
// A file change under tool-approval.
|
|
1905
|
+
editRejected: (path) => `Your organization's protection requires a person to approve every change, and the user denied this change to ${path}. The file was not changed. Do not change it another way unless the user asks.`,
|
|
1906
|
+
editTimedOut: (path, minutes) => `Your organization's protection requires a person to approve every change, and nobody answered about ${path} within ${minutes} minutes. The file was not changed. Ask the user before trying again.`,
|
|
1907
|
+
editStopped: "The request was stopped before the file was changed.",
|
|
1908
|
+
editUnavailable: (path) => `Your organization's protection requires a person to approve every change, and the approval for ${path} could not be requested. The file was not changed. Tell the user; do not change it another way.`
|
|
1900
1909
|
};
|
|
1901
1910
|
|
|
1902
1911
|
// src/application/services/workspaceSandbox/SessionTransport.ts
|
|
@@ -3031,6 +3040,15 @@ var BrowserCommandGate = class {
|
|
|
3031
3040
|
this.chain = next.catch(() => void 0);
|
|
3032
3041
|
return next;
|
|
3033
3042
|
}
|
|
3043
|
+
checkProtectedEdit(path, opts = {}) {
|
|
3044
|
+
const next = this.chain.then(() => this.ask(`edit ${path}`, {
|
|
3045
|
+
signal: opts.signal,
|
|
3046
|
+
edit: { path, ...opts.preview ? { preview: opts.preview } : {} },
|
|
3047
|
+
protection: { reasons: ["PROTECTION_TOOL_APPROVAL"] }
|
|
3048
|
+
}));
|
|
3049
|
+
this.chain = next.catch(() => void 0);
|
|
3050
|
+
return next;
|
|
3051
|
+
}
|
|
3034
3052
|
checkEdit(path, opts = {}) {
|
|
3035
3053
|
const next = this.chain.then(() => this.ask(`edit ${path}`, { signal: opts.signal, edit: { path, ...opts.preview ? { preview: opts.preview } : {} } }));
|
|
3036
3054
|
this.chain = next.catch(() => void 0);
|
|
@@ -3047,7 +3065,7 @@ var BrowserCommandGate = class {
|
|
|
3047
3065
|
command,
|
|
3048
3066
|
cwd: this.o.root,
|
|
3049
3067
|
...opts.description ? { description: opts.description.slice(0, 1e3) } : {},
|
|
3050
|
-
reasons:
|
|
3068
|
+
reasons: protection ? [...protection.reasons, ...opts.edit ? [] : approvalReasons(command)] : opts.edit ? [] : approvalReasons(command),
|
|
3051
3069
|
suggestedRule: opts.edit || protection ? null : suggestRule(command),
|
|
3052
3070
|
...opts.edit ? { edit: { path: opts.edit.path, ...opts.edit.preview ? { preview: opts.edit.preview.slice(0, 4e3) } : {} } } : {},
|
|
3053
3071
|
...protection?.package ? { package: protection.package, ...protection.packageReason ? { packageReason: protection.packageReason } : {} } : {}
|
|
@@ -3055,6 +3073,7 @@ var BrowserCommandGate = class {
|
|
|
3055
3073
|
} catch (e) {
|
|
3056
3074
|
this.o.log?.(`command approval could not be requested: ${e.message}`);
|
|
3057
3075
|
const code = e instanceof CommandApprovalError ? e.code : null;
|
|
3076
|
+
if (protection && opts.edit) return { allow: false, message: PROTECTION_MESSAGES.editUnavailable(opts.edit.path) };
|
|
3058
3077
|
if (protection && code !== "COMMAND_NOT_ALLOWED") return { allow: false, message: PROTECTION_MESSAGES.unavailable };
|
|
3059
3078
|
return {
|
|
3060
3079
|
allow: false,
|
|
@@ -3072,10 +3091,12 @@ var BrowserCommandGate = class {
|
|
|
3072
3091
|
if (!decision) {
|
|
3073
3092
|
const reason = opts.signal?.aborted ? "STOPPED" : "TIMED_OUT";
|
|
3074
3093
|
void this.o.api.cancel(approvalId, reason).catch(() => void 0);
|
|
3094
|
+
if (protection && opts.edit) return { allow: false, message: reason === "STOPPED" ? PROTECTION_MESSAGES.editStopped : PROTECTION_MESSAGES.editTimedOut(opts.edit.path, Math.round(timeoutMs / 6e4)) };
|
|
3075
3095
|
if (protection) return { allow: false, message: reason === "STOPPED" ? PROTECTION_MESSAGES.stopped : PROTECTION_MESSAGES.timedOut(Math.round(timeoutMs / 6e4)) };
|
|
3076
3096
|
return reason === "STOPPED" ? { allow: false, message: STOPPED } : { allow: false, message: `The user did not answer the approval for this command within ${Math.round(timeoutMs / 6e4)} minutes, so it did not run. Ask the user before trying it again.` };
|
|
3077
3097
|
}
|
|
3078
3098
|
if (decision.decision === "REJECT") {
|
|
3099
|
+
if (protection && opts.edit) return { allow: false, message: PROTECTION_MESSAGES.editRejected(opts.edit.path) };
|
|
3079
3100
|
if (protection) return { allow: false, message: PROTECTION_MESSAGES.rejected };
|
|
3080
3101
|
return opts.edit ? { allow: false, message: `The user declined this change to ${opts.edit.path} in the browser. The file was not changed. Ask the user what to do differently.` } : { allow: false, message: "The user denied this command in the browser. It did not run. Do not run the same command again unless the user asks; continue another way or ask the user." };
|
|
3081
3102
|
}
|
|
@@ -4821,6 +4842,8 @@ function sandboxTestEnv() {
|
|
|
4821
4842
|
"BUNDLE_PATH",
|
|
4822
4843
|
"PUB_CACHE",
|
|
4823
4844
|
"FLUTTER_ROOT",
|
|
4845
|
+
"MIX_HOME",
|
|
4846
|
+
"HEX_HOME",
|
|
4824
4847
|
"MISE_DATA_DIR",
|
|
4825
4848
|
"MISE_CACHE_DIR",
|
|
4826
4849
|
"ELIXIR_ERL_OPTIONS"
|
|
@@ -4943,6 +4966,8 @@ var ENGINE_MESSAGES = {
|
|
|
4943
4966
|
// Pull requests.
|
|
4944
4967
|
BASE_ADVANCED: "The pull request could not be opened: the base branch in the repository is not at the commit this change was made on.",
|
|
4945
4968
|
PR_FAILED: "The pull request could not be opened.",
|
|
4969
|
+
PROVIDER_CONNECTION_REQUIRED: "The pull request could not be opened: the provider connection of {repoFullName} needs to be reconnected in ScaleQuality.",
|
|
4970
|
+
REPOSITORY_NOT_IN_PROJECT: "The pull request could not be opened: {repoFullName} is no longer a repository of this project.",
|
|
4946
4971
|
// Attachments and mentions.
|
|
4947
4972
|
ATTACHMENT_SKIPPED: "An attached image could not be read and was left out.",
|
|
4948
4973
|
MENTION_SKIPPED: "{path} could not be attached ({reason}).",
|
|
@@ -5176,7 +5201,7 @@ function limitInEngineText(text3) {
|
|
|
5176
5201
|
// src/application/services/workspaceSandbox/scalequalityTools.ts
|
|
5177
5202
|
var import_crypto4 = require("crypto");
|
|
5178
5203
|
|
|
5179
|
-
// node_modules/zod/v3/external.js
|
|
5204
|
+
// ../../scalequality-ai-governance-service/node_modules/zod/v3/external.js
|
|
5180
5205
|
var external_exports = {};
|
|
5181
5206
|
__export(external_exports, {
|
|
5182
5207
|
BRAND: () => BRAND,
|
|
@@ -5288,7 +5313,7 @@ __export(external_exports, {
|
|
|
5288
5313
|
void: () => voidType
|
|
5289
5314
|
});
|
|
5290
5315
|
|
|
5291
|
-
// node_modules/zod/v3/helpers/util.js
|
|
5316
|
+
// ../../scalequality-ai-governance-service/node_modules/zod/v3/helpers/util.js
|
|
5292
5317
|
var util;
|
|
5293
5318
|
(function(util2) {
|
|
5294
5319
|
util2.assertEqual = (_) => {
|
|
@@ -5422,7 +5447,7 @@ var getParsedType = (data) => {
|
|
|
5422
5447
|
}
|
|
5423
5448
|
};
|
|
5424
5449
|
|
|
5425
|
-
// node_modules/zod/v3/ZodError.js
|
|
5450
|
+
// ../../scalequality-ai-governance-service/node_modules/zod/v3/ZodError.js
|
|
5426
5451
|
var ZodIssueCode = util.arrayToEnum([
|
|
5427
5452
|
"invalid_type",
|
|
5428
5453
|
"invalid_literal",
|
|
@@ -5540,7 +5565,7 @@ ZodError.create = (issues) => {
|
|
|
5540
5565
|
return error;
|
|
5541
5566
|
};
|
|
5542
5567
|
|
|
5543
|
-
// node_modules/zod/v3/locales/en.js
|
|
5568
|
+
// ../../scalequality-ai-governance-service/node_modules/zod/v3/locales/en.js
|
|
5544
5569
|
var errorMap = (issue, _ctx) => {
|
|
5545
5570
|
let message;
|
|
5546
5571
|
switch (issue.code) {
|
|
@@ -5643,7 +5668,7 @@ var errorMap = (issue, _ctx) => {
|
|
|
5643
5668
|
};
|
|
5644
5669
|
var en_default = errorMap;
|
|
5645
5670
|
|
|
5646
|
-
// node_modules/zod/v3/errors.js
|
|
5671
|
+
// ../../scalequality-ai-governance-service/node_modules/zod/v3/errors.js
|
|
5647
5672
|
var overrideErrorMap = en_default;
|
|
5648
5673
|
function setErrorMap(map) {
|
|
5649
5674
|
overrideErrorMap = map;
|
|
@@ -5652,7 +5677,7 @@ function getErrorMap() {
|
|
|
5652
5677
|
return overrideErrorMap;
|
|
5653
5678
|
}
|
|
5654
5679
|
|
|
5655
|
-
// node_modules/zod/v3/helpers/parseUtil.js
|
|
5680
|
+
// ../../scalequality-ai-governance-service/node_modules/zod/v3/helpers/parseUtil.js
|
|
5656
5681
|
var makeIssue = (params) => {
|
|
5657
5682
|
const { data, path, errorMaps, issueData } = params;
|
|
5658
5683
|
const fullPath = [...path, ...issueData.path || []];
|
|
@@ -5762,14 +5787,14 @@ var isDirty = (x) => x.status === "dirty";
|
|
|
5762
5787
|
var isValid = (x) => x.status === "valid";
|
|
5763
5788
|
var isAsync = (x) => typeof Promise !== "undefined" && x instanceof Promise;
|
|
5764
5789
|
|
|
5765
|
-
// node_modules/zod/v3/helpers/errorUtil.js
|
|
5790
|
+
// ../../scalequality-ai-governance-service/node_modules/zod/v3/helpers/errorUtil.js
|
|
5766
5791
|
var errorUtil;
|
|
5767
5792
|
(function(errorUtil2) {
|
|
5768
5793
|
errorUtil2.errToObj = (message) => typeof message === "string" ? { message } : message || {};
|
|
5769
5794
|
errorUtil2.toString = (message) => typeof message === "string" ? message : message?.message;
|
|
5770
5795
|
})(errorUtil || (errorUtil = {}));
|
|
5771
5796
|
|
|
5772
|
-
// node_modules/zod/v3/types.js
|
|
5797
|
+
// ../../scalequality-ai-governance-service/node_modules/zod/v3/types.js
|
|
5773
5798
|
var ParseInputLazyPath = class {
|
|
5774
5799
|
constructor(parent, value, path, key) {
|
|
5775
5800
|
this._cachedPath = [];
|
|
@@ -9615,7 +9640,8 @@ var SdkEventMapper = class {
|
|
|
9615
9640
|
startedAt: iso(startedAt)
|
|
9616
9641
|
} });
|
|
9617
9642
|
const target = activityTarget(name, input);
|
|
9618
|
-
|
|
9643
|
+
const description = d.code === "STEP_COMMAND_DESCRIBED" && typeof d.params?.description === "string" ? d.params.description : void 0;
|
|
9644
|
+
this.cb.emit({ type: "activity", data: { tool: name, ...target ? { target } : {}, ...description ? { description } : {} } });
|
|
9619
9645
|
}
|
|
9620
9646
|
stepDone(id, t, status, endedAt = this.now()) {
|
|
9621
9647
|
this.cb.emit({ type: "step", data: {
|
|
@@ -9826,7 +9852,8 @@ function activityTarget(name, input) {
|
|
|
9826
9852
|
for (const part of s("command").split(/&&|\|\||[;|\n]/)) {
|
|
9827
9853
|
for (const token of part.trim().split(/\s+/)) {
|
|
9828
9854
|
if (!token || token.includes("=") || COMMAND_WRAPPERS.has(token)) continue;
|
|
9829
|
-
if (
|
|
9855
|
+
if (SHELL_PREFIX.has(token)) continue;
|
|
9856
|
+
if (COMMAND_SETUP.has(token) || SHELL_HEADER.has(token)) break;
|
|
9830
9857
|
if (!/^[A-Za-z0-9._/-]+$/.test(token)) return void 0;
|
|
9831
9858
|
const word = token.split("/").filter(Boolean).pop() ?? "";
|
|
9832
9859
|
return word && word.length <= 40 ? word : void 0;
|
|
@@ -9837,6 +9864,8 @@ function activityTarget(name, input) {
|
|
|
9837
9864
|
}
|
|
9838
9865
|
var COMMAND_WRAPPERS = /* @__PURE__ */ new Set(["sudo", "env", "time", "nice", "exec", "command", "nohup"]);
|
|
9839
9866
|
var COMMAND_SETUP = /* @__PURE__ */ new Set(["cd", "pushd", "popd", "export", "source", ".", "set", "unset"]);
|
|
9867
|
+
var SHELL_HEADER = /* @__PURE__ */ new Set(["for", "case", "select", "[", "[[", "test", "true", "false", ":"]);
|
|
9868
|
+
var SHELL_PREFIX = /* @__PURE__ */ new Set(["do", "then", "else", "elif", "if", "while", "until", "done", "fi", "esac", "!", "{", "}", "(", ")"]);
|
|
9840
9869
|
function blockText(content) {
|
|
9841
9870
|
if (typeof content === "string") return content;
|
|
9842
9871
|
if (Array.isArray(content)) {
|
|
@@ -10000,12 +10029,16 @@ var TurnTiming = class {
|
|
|
10000
10029
|
// src/application/services/workspaceSandbox/protectionApproval.ts
|
|
10001
10030
|
function gateProtectionApprover(gate) {
|
|
10002
10031
|
if (!gate.checkProtected) return null;
|
|
10003
|
-
return {
|
|
10032
|
+
return {
|
|
10033
|
+
ask: (command, check) => gate.checkProtected(command, check),
|
|
10034
|
+
...gate.checkProtectedEdit ? { askEdit: (path, check) => gate.checkProtectedEdit(path, check) } : {}
|
|
10035
|
+
};
|
|
10004
10036
|
}
|
|
10005
10037
|
function cloudProtectionApprover(o) {
|
|
10006
10038
|
let chain = Promise.resolve();
|
|
10007
10039
|
const ask = async (command, check) => {
|
|
10008
|
-
|
|
10040
|
+
const edit = check.edit;
|
|
10041
|
+
if (check.signal?.aborted) return { allow: false, message: edit ? PROTECTION_MESSAGES.editStopped : PROTECTION_MESSAGES.stopped };
|
|
10009
10042
|
let approvalId;
|
|
10010
10043
|
try {
|
|
10011
10044
|
({ approvalId } = await o.calls.requestCommandApproval({
|
|
@@ -10014,11 +10047,12 @@ function cloudProtectionApprover(o) {
|
|
|
10014
10047
|
...check.description ? { description: check.description.slice(0, 1e3) } : {},
|
|
10015
10048
|
reasons: [...check.reasons],
|
|
10016
10049
|
suggestedRule: null,
|
|
10050
|
+
...edit ? { edit: { path: edit.path, ...edit.preview ? { preview: edit.preview.slice(0, 4e3) } : {} } } : {},
|
|
10017
10051
|
...check.package ? { package: check.package, ...check.packageReason ? { packageReason: check.packageReason } : {} } : {}
|
|
10018
10052
|
}));
|
|
10019
10053
|
} catch (e) {
|
|
10020
10054
|
o.log?.(`protection approval could not be requested: ${e.message}`);
|
|
10021
|
-
return { allow: false, message: PROTECTION_MESSAGES.unavailable };
|
|
10055
|
+
return { allow: false, message: edit ? PROTECTION_MESSAGES.editUnavailable(edit.path) : PROTECTION_MESSAGES.unavailable };
|
|
10022
10056
|
}
|
|
10023
10057
|
const timeoutMs = o.timeoutMs ?? COMMAND_APPROVAL_TIMEOUT_MS;
|
|
10024
10058
|
const timer = new AbortController();
|
|
@@ -10029,16 +10063,21 @@ function cloudProtectionApprover(o) {
|
|
|
10029
10063
|
if (!decision) {
|
|
10030
10064
|
const reason = check.signal?.aborted ? "STOPPED" : "TIMED_OUT";
|
|
10031
10065
|
void o.calls.cancelCommandApproval(approvalId, reason).catch(() => void 0);
|
|
10032
|
-
|
|
10066
|
+
const minutes = Math.round(timeoutMs / 6e4);
|
|
10067
|
+
if (edit) return { allow: false, message: reason === "STOPPED" ? PROTECTION_MESSAGES.editStopped : PROTECTION_MESSAGES.editTimedOut(edit.path, minutes) };
|
|
10068
|
+
return { allow: false, message: reason === "STOPPED" ? PROTECTION_MESSAGES.stopped : PROTECTION_MESSAGES.timedOut(minutes) };
|
|
10033
10069
|
}
|
|
10034
|
-
|
|
10070
|
+
if (decision.decision === "APPROVE") return { allow: true };
|
|
10071
|
+
return { allow: false, message: edit ? PROTECTION_MESSAGES.editRejected(edit.path) : PROTECTION_MESSAGES.rejected };
|
|
10072
|
+
};
|
|
10073
|
+
const queued = (run2) => {
|
|
10074
|
+
const next = chain.then(run2);
|
|
10075
|
+
chain = next.catch(() => void 0);
|
|
10076
|
+
return next;
|
|
10035
10077
|
};
|
|
10036
10078
|
return {
|
|
10037
|
-
ask(command, check)
|
|
10038
|
-
|
|
10039
|
-
chain = next.catch(() => void 0);
|
|
10040
|
-
return next;
|
|
10041
|
-
}
|
|
10079
|
+
ask: (command, check) => queued(() => ask(command, check)),
|
|
10080
|
+
askEdit: (path, check) => queued(() => ask(`edit ${path}`, { reasons: ["PROTECTION_TOOL_APPROVAL"], signal: check.signal, edit: { path, ...check.preview ? { preview: check.preview } : {} } }))
|
|
10042
10081
|
};
|
|
10043
10082
|
}
|
|
10044
10083
|
|
|
@@ -13150,16 +13189,33 @@ _Measured on an earlier version of this change._`);
|
|
|
13150
13189
|
if (e instanceof TransportError && e.code === REPOSITORY_NOT_IN_SCOPE) {
|
|
13151
13190
|
return text(`${REPOSITORY_NOT_IN_SCOPE}: ${target} left this session's scope before the pull request was opened. It was not opened.`, true);
|
|
13152
13191
|
}
|
|
13153
|
-
|
|
13154
|
-
|
|
13155
|
-
|
|
13156
|
-
return text(`The pull request could not be opened: the base branch in the repository is not at the commit this change was made on.${where} Tell the user; do not retry without being asked.`, true);
|
|
13157
|
-
}
|
|
13158
|
-
this.error("PR_FAILED", { repoFullName: target });
|
|
13159
|
-
return text("The pull request could not be opened. Tell the user; do not retry without being asked.", true);
|
|
13192
|
+
const failure = pullRequestFailure(status, e instanceof TransportError ? e.code : void 0, target, this.local);
|
|
13193
|
+
this.error(failure.code, failure.params);
|
|
13194
|
+
return text(`${failure.say} Tell the user; do not retry without being asked.`, true);
|
|
13160
13195
|
}
|
|
13161
13196
|
}
|
|
13162
13197
|
};
|
|
13198
|
+
function pullRequestFailure(status, code, repoFullName, local) {
|
|
13199
|
+
if (status === 409 && code === "PROVIDER_CONNECTION_REQUIRED") return {
|
|
13200
|
+
code: "PROVIDER_CONNECTION_REQUIRED",
|
|
13201
|
+
params: { repoFullName },
|
|
13202
|
+
say: `The pull request could not be opened: the provider connection of ${repoFullName} needs to be reconnected in ScaleQuality.`
|
|
13203
|
+
};
|
|
13204
|
+
if (status === 409 && code === "REPOSITORY_NOT_IN_PROJECT") return {
|
|
13205
|
+
code: "REPOSITORY_NOT_IN_PROJECT",
|
|
13206
|
+
params: { repoFullName },
|
|
13207
|
+
say: `The pull request could not be opened: ${repoFullName} is no longer a repository of this project.`
|
|
13208
|
+
};
|
|
13209
|
+
if (status === 409 && (!code || code === "BASE_ADVANCED")) {
|
|
13210
|
+
const where = local ? " Update this folder to the latest commit of the base branch (git pull), then ask again." : "";
|
|
13211
|
+
return {
|
|
13212
|
+
code: "BASE_ADVANCED",
|
|
13213
|
+
params: { repoFullName, local },
|
|
13214
|
+
say: `The pull request could not be opened: the base branch in the repository is not at the commit this change was made on.${where}`
|
|
13215
|
+
};
|
|
13216
|
+
}
|
|
13217
|
+
return { code: "PR_FAILED", params: { repoFullName, ...code ? { reason: code } : {} }, say: "The pull request could not be opened." };
|
|
13218
|
+
}
|
|
13163
13219
|
var LOCAL_ENV_PASSTHROUGH = [
|
|
13164
13220
|
"LOGNAME",
|
|
13165
13221
|
"COLORTERM",
|
|
@@ -13312,11 +13368,11 @@ function buildQueryOptions(o) {
|
|
|
13312
13368
|
}
|
|
13313
13369
|
const protections = o.protections?.() ?? NO_PROTECTIONS;
|
|
13314
13370
|
const d = await decideToolUse(name, input.tool_input ?? {}, { ...o.policy, protections });
|
|
13315
|
-
const guarded = d.behavior === "allow" && name === "Bash" && mayNeedProtection(String(d.updatedInput.command ?? ""), protections);
|
|
13371
|
+
const guarded = d.behavior === "allow" && (name === "Bash" && mayNeedProtection(String(d.updatedInput.command ?? ""), protections) || protections.toolApproval && !o.plan && name in WRITE_TOOL_PATHS);
|
|
13316
13372
|
const gated = guarded || d.behavior === "allow" && !!gate && (name === "Bash" || !!gate.checkEdit && !o.plan && name in WRITE_TOOL_PATHS);
|
|
13317
13373
|
if (d.behavior === "allow" && !gated) await beforeWrite(name, d.updatedInput);
|
|
13318
13374
|
if (gated) {
|
|
13319
|
-
return { hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "ask", permissionDecisionReason: guarded ? "This
|
|
13375
|
+
return { hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "ask", permissionDecisionReason: guarded ? "This action follows the organization's protections." : "Commands and changes on this machine follow the person's permission mode." } };
|
|
13320
13376
|
}
|
|
13321
13377
|
return d.behavior === "allow" ? { hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "allow", updatedInput: d.updatedInput } } : { hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "deny", permissionDecisionReason: d.message } };
|
|
13322
13378
|
};
|
|
@@ -13354,10 +13410,18 @@ function buildQueryOptions(o) {
|
|
|
13354
13410
|
protectionApproved = true;
|
|
13355
13411
|
}
|
|
13356
13412
|
}
|
|
13357
|
-
|
|
13413
|
+
const changes = !o.plan && toolName in WRITE_TOOL_PATHS;
|
|
13414
|
+
const changePath = () => {
|
|
13358
13415
|
const field = WRITE_TOOL_PATHS[toolName];
|
|
13359
|
-
|
|
13360
|
-
|
|
13416
|
+
return typeof d.updatedInput[field] === "string" ? (0, import_path15.relative)(o.root, d.updatedInput[field]) || String(d.updatedInput[field]) : "";
|
|
13417
|
+
};
|
|
13418
|
+
if (changes && protections.toolApproval) {
|
|
13419
|
+
const path = changePath(), askEdit = o.protection?.approver?.askEdit;
|
|
13420
|
+
if (!askEdit) return { behavior: "deny", message: PROTECTION_MESSAGES.editUnavailable(path) };
|
|
13421
|
+
const r = await askEdit(path, { preview: editPreview(toolName, d.updatedInput), signal: opts?.signal ?? o.abortController.signal });
|
|
13422
|
+
if (!r.allow) return { behavior: "deny", message: r.message };
|
|
13423
|
+
} else if (gate?.checkEdit && changes) {
|
|
13424
|
+
const r = await gate.checkEdit(changePath(), { preview: editPreview(toolName, d.updatedInput), signal: opts?.signal ?? o.abortController.signal });
|
|
13361
13425
|
if (!r.allow) return { behavior: "deny", message: r.message };
|
|
13362
13426
|
}
|
|
13363
13427
|
await beforeWrite(toolName, d.updatedInput);
|
|
@@ -13420,7 +13484,8 @@ function timedGate(gate, took) {
|
|
|
13420
13484
|
return {
|
|
13421
13485
|
check: (command, opts) => timed(() => gate.check(command, opts)),
|
|
13422
13486
|
...gate.checkEdit ? { checkEdit: (path, opts) => timed(() => gate.checkEdit(path, opts)) } : {},
|
|
13423
|
-
...gate.checkProtected ? { checkProtected: (command, opts) => timed(() => gate.checkProtected(command, opts)) } : {}
|
|
13487
|
+
...gate.checkProtected ? { checkProtected: (command, opts) => timed(() => gate.checkProtected(command, opts)) } : {},
|
|
13488
|
+
...gate.checkProtectedEdit ? { checkProtectedEdit: (path, opts) => timed(() => gate.checkProtectedEdit(path, opts)) } : {}
|
|
13424
13489
|
};
|
|
13425
13490
|
}
|
|
13426
13491
|
async function hasLocalTranscript(configDir, sessionId) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scalequality/cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.12",
|
|
4
4
|
"description": "ScaleQuality CLI. Connect your computer to the ScaleQuality AI Workspace (`scalequality login`), run its coding engine in any folder of your computer, and import your Claude Code and Codex conversations.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|