@gravity-rail/cli 0.22.0 → 0.32.0
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/README.md +26 -7
- package/dist/{account-verify-phone-QJDC57CB.js → account-verify-phone-5DY6ETMV.js} +3 -3
- package/dist/{chunk-IYCL6P6B.js → chunk-IGZVMTG5.js} +212 -39
- package/dist/chunk-IGZVMTG5.js.map +1 -0
- package/dist/{chunk-EGO3LMNZ.js → chunk-KHUZAYUS.js} +14 -10
- package/dist/chunk-KHUZAYUS.js.map +1 -0
- package/dist/{cli-client-context-dl4gw0Fb.d.ts → cli-client-context-cd5f2JKX.d.ts} +46 -2
- package/dist/cli-utils.d.ts +13 -2
- package/dist/cli-utils.js +7 -1
- package/dist/gr.js +6098 -515
- package/dist/gr.js.map +1 -1
- package/dist/lib.d.ts +15 -3
- package/dist/lib.js +2586 -427
- package/dist/lib.js.map +1 -1
- package/package.json +3 -2
- package/dist/chunk-EGO3LMNZ.js.map +0 -1
- package/dist/chunk-IYCL6P6B.js.map +0 -1
- /package/dist/{account-verify-phone-QJDC57CB.js.map → account-verify-phone-5DY6ETMV.js.map} +0 -0
package/README.md
CHANGED
|
@@ -244,13 +244,19 @@ cat member.json | gr members update -w $WORKSPACE --id 42 --allow-write
|
|
|
244
244
|
| `3` | A human-in-the-loop (HITL) interrupt requires out-of-band action before the operation can continue |
|
|
245
245
|
| `4` | No assistant reply — a `--message` turn made tool calls but returned no final summary, or an inference error occurred with no final message (side effects may have been applied) |
|
|
246
246
|
|
|
247
|
-
**UI
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
the
|
|
247
|
+
**UI prompts** (`present_choice_list` / `render_ui`, and the `connect_*_account`
|
|
248
|
+
login prompts) do not pause the agent. The tool returns straight away and the
|
|
249
|
+
prompt arrives as a tool result, which the CLI draws in the terminal when stdin
|
|
250
|
+
is a TTY: OpenUI markup becomes an arrow-key picker (ChoiceButtons, with Space
|
|
251
|
+
to tick a multi-select) or field-by-field form prompts, and a connect prompt
|
|
252
|
+
prints the link to open. Your answer is then sent as an ordinary chat message,
|
|
253
|
+
so the agent picks it up on its next turn. Manager (`gr manage`) and Concierge
|
|
254
|
+
(`gr concierge chat`) share this path. Typing prose instead of using the
|
|
255
|
+
controls sends exactly what you typed. When stdin is not a TTY the prompt is
|
|
256
|
+
printed and left unanswered, and the turn ends normally.
|
|
257
|
+
|
|
258
|
+
Approvals, access grants and checkout still **interrupt** the run and resume it
|
|
259
|
+
in place; unsupported interrupt types exit `3` with the JSON contract below.
|
|
254
260
|
|
|
255
261
|
When a concierge command run with `--message` (non-interactive mode) hits a HITL
|
|
256
262
|
interrupt the CLI cannot resolve on its own, it exits `3` and writes a single
|
|
@@ -439,6 +445,19 @@ gr workspaces get -w $SOURCE_WORKSPACE -o json | \
|
|
|
439
445
|
gr workspaces create --data "$(cat -)" --allow-write
|
|
440
446
|
```
|
|
441
447
|
|
|
448
|
+
### Check configuration readiness
|
|
449
|
+
|
|
450
|
+
```sh
|
|
451
|
+
gr workspaces configuration-readiness -w $WORKSPACE -o json
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
The combined read fails closed in its `configurationReady` field when
|
|
455
|
+
configuration-health collectors fail, critical or warning findings remain,
|
|
456
|
+
post-import bindings are missing, skipped EventRules remain outstanding, or a
|
|
457
|
+
readiness scan is truncated. Informational configuration findings are reported
|
|
458
|
+
but do not block. This is a saved-configuration check, not an end-to-end launch
|
|
459
|
+
or patient-experience certification.
|
|
460
|
+
|
|
442
461
|
### List all workflows with their task counts
|
|
443
462
|
|
|
444
463
|
```sh
|
|
@@ -2,8 +2,8 @@ import {
|
|
|
2
2
|
confirmAccountPhoneVerify,
|
|
3
3
|
requireAuth,
|
|
4
4
|
startAccountPhoneVerify
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-
|
|
5
|
+
} from "./chunk-KHUZAYUS.js";
|
|
6
|
+
import "./chunk-IGZVMTG5.js";
|
|
7
7
|
import "./chunk-3T7A5PSF.js";
|
|
8
8
|
|
|
9
9
|
// src/commands/account-verify-phone.ts
|
|
@@ -38,4 +38,4 @@ async function promptLine(question) {
|
|
|
38
38
|
export {
|
|
39
39
|
accountVerifyPhoneCommand
|
|
40
40
|
};
|
|
41
|
-
//# sourceMappingURL=account-verify-phone-
|
|
41
|
+
//# sourceMappingURL=account-verify-phone-5DY6ETMV.js.map
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// src/cli-utils/config.ts
|
|
2
|
-
import { existsSync, readFileSync } from "fs";
|
|
2
|
+
import { existsSync, readFileSync, statSync } from "fs";
|
|
3
|
+
import { CORE_SCHEMA, defineSequenceTag, load } from "js-yaml";
|
|
3
4
|
import { dirname, join, resolve } from "path";
|
|
4
5
|
var ENVIRONMENTS = {
|
|
5
6
|
dev: {
|
|
@@ -18,46 +19,174 @@ var ENVIRONMENTS = {
|
|
|
18
19
|
var CALLBACK_PORT = 31337;
|
|
19
20
|
var CALLBACK_PATH = "/oauth/callback";
|
|
20
21
|
var REDIRECT_URI = `http://localhost:${CALLBACK_PORT}${CALLBACK_PATH}`;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
var API_CONTAINER_PORT = 443;
|
|
23
|
+
var FRONTEND_CONTAINER_PORT = 3e3;
|
|
24
|
+
var WORKTREE_SETUP_MARKER = "worktree-setup.sh";
|
|
25
|
+
var OVERRIDE_FILENAME = "docker-compose.override.yml";
|
|
26
|
+
var OVERRIDE_SEQUENCE_TAG = defineSequenceTag("!override", {
|
|
27
|
+
create: () => [],
|
|
28
|
+
addItem: (carrier, item) => {
|
|
29
|
+
carrier.push(item);
|
|
30
|
+
},
|
|
31
|
+
identify: () => false
|
|
32
|
+
});
|
|
33
|
+
var COMPOSE_OVERRIDE_SCHEMA = CORE_SCHEMA.withTags(OVERRIDE_SEQUENCE_TAG);
|
|
34
|
+
var WorktreeConfigError = class extends Error {
|
|
35
|
+
constructor(message, checkoutRoot, overridePath) {
|
|
36
|
+
super(message);
|
|
37
|
+
this.checkoutRoot = checkoutRoot;
|
|
38
|
+
this.overridePath = overridePath;
|
|
39
|
+
this.name = "WorktreeConfigError";
|
|
40
|
+
}
|
|
41
|
+
checkoutRoot;
|
|
42
|
+
overridePath;
|
|
43
|
+
};
|
|
44
|
+
function isRecord(value) {
|
|
45
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
46
|
+
}
|
|
47
|
+
function parsePortToken(token) {
|
|
48
|
+
if (token === void 0 || !/^\d+$/.test(token)) return void 0;
|
|
49
|
+
const value = Number(token);
|
|
50
|
+
if (!Number.isInteger(value) || value <= 0 || value > 65535) return void 0;
|
|
51
|
+
return value;
|
|
52
|
+
}
|
|
53
|
+
function parsePublishedTarget(mapping) {
|
|
54
|
+
const parts = mapping.split(":");
|
|
55
|
+
if (parts.length < 2) return void 0;
|
|
56
|
+
const target = parsePortToken(parts[parts.length - 1]);
|
|
57
|
+
const published = parsePortToken(parts[parts.length - 2]);
|
|
58
|
+
if (target === void 0 || published === void 0) return void 0;
|
|
59
|
+
return { published, target };
|
|
60
|
+
}
|
|
61
|
+
function publishedPortForTarget(ports, target) {
|
|
62
|
+
if (!Array.isArray(ports)) return void 0;
|
|
63
|
+
for (const entry of ports) {
|
|
64
|
+
if (typeof entry !== "string") continue;
|
|
65
|
+
const parsed = parsePublishedTarget(entry);
|
|
66
|
+
if (parsed?.target === target) return parsed.published;
|
|
67
|
+
}
|
|
68
|
+
return void 0;
|
|
69
|
+
}
|
|
70
|
+
function servicePorts(services, name) {
|
|
71
|
+
const service = services[name];
|
|
72
|
+
if (!isRecord(service)) return void 0;
|
|
73
|
+
return service["ports"];
|
|
74
|
+
}
|
|
75
|
+
function findNearestGit(startDir) {
|
|
76
|
+
let dir = resolve(startDir);
|
|
24
77
|
while (true) {
|
|
25
|
-
const
|
|
26
|
-
if (existsSync(
|
|
27
|
-
|
|
28
|
-
|
|
78
|
+
const gitPath = join(dir, ".git");
|
|
79
|
+
if (existsSync(gitPath)) {
|
|
80
|
+
try {
|
|
81
|
+
const st = statSync(gitPath);
|
|
82
|
+
return { root: dir, gitIsFile: st.isFile() };
|
|
83
|
+
} catch {
|
|
84
|
+
}
|
|
29
85
|
}
|
|
30
86
|
const parent = dirname(dir);
|
|
31
|
-
if (parent === dir)
|
|
87
|
+
if (parent === dir) return void 0;
|
|
32
88
|
dir = parent;
|
|
33
89
|
}
|
|
34
|
-
|
|
90
|
+
}
|
|
91
|
+
function unresolved(root, overridePath, reason) {
|
|
92
|
+
return { kind: "unresolved-worktree", root, overridePath, reason };
|
|
93
|
+
}
|
|
94
|
+
function loadComposeDocument(content) {
|
|
95
|
+
let loaded;
|
|
96
|
+
try {
|
|
97
|
+
loaded = load(content, { schema: COMPOSE_OVERRIDE_SCHEMA });
|
|
98
|
+
} catch {
|
|
99
|
+
return void 0;
|
|
100
|
+
}
|
|
101
|
+
return isRecord(loaded) ? loaded : void 0;
|
|
102
|
+
}
|
|
103
|
+
function classifyCheckout(startDir = process.cwd()) {
|
|
104
|
+
const git = findNearestGit(startDir);
|
|
105
|
+
if (!git || !git.gitIsFile) {
|
|
106
|
+
return { kind: "non-worktree", root: git?.root };
|
|
107
|
+
}
|
|
108
|
+
const overridePath = join(git.root, OVERRIDE_FILENAME);
|
|
109
|
+
if (!existsSync(overridePath)) {
|
|
110
|
+
return unresolved(
|
|
111
|
+
git.root,
|
|
112
|
+
overridePath,
|
|
113
|
+
`docker-compose.override.yml (generated by ${WORKTREE_SETUP_MARKER})`
|
|
114
|
+
);
|
|
115
|
+
}
|
|
35
116
|
let content;
|
|
36
117
|
try {
|
|
37
118
|
content = readFileSync(overridePath, "utf-8");
|
|
38
119
|
} catch {
|
|
39
|
-
return
|
|
40
|
-
}
|
|
41
|
-
if (!content.includes(
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
120
|
+
return unresolved(git.root, overridePath, "readable generated worktree override");
|
|
121
|
+
}
|
|
122
|
+
if (!content.includes(WORKTREE_SETUP_MARKER)) {
|
|
123
|
+
return unresolved(
|
|
124
|
+
git.root,
|
|
125
|
+
overridePath,
|
|
126
|
+
`generated ${WORKTREE_SETUP_MARKER} override marker`
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
const document = loadComposeDocument(content);
|
|
130
|
+
if (!document) {
|
|
131
|
+
return unresolved(git.root, overridePath, "valid generated worktree override YAML");
|
|
132
|
+
}
|
|
133
|
+
const services = document["services"];
|
|
134
|
+
if (!isRecord(services)) {
|
|
135
|
+
return unresolved(
|
|
136
|
+
git.root,
|
|
137
|
+
overridePath,
|
|
138
|
+
`api-nginx published port for container ${API_CONTAINER_PORT}; frontend published port for container ${FRONTEND_CONTAINER_PORT}`
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
const apiPort = publishedPortForTarget(servicePorts(services, "api-nginx"), API_CONTAINER_PORT);
|
|
142
|
+
const frontendPort = publishedPortForTarget(
|
|
143
|
+
servicePorts(services, "frontend"),
|
|
144
|
+
FRONTEND_CONTAINER_PORT
|
|
145
|
+
);
|
|
146
|
+
const missing = [];
|
|
147
|
+
if (apiPort === void 0) {
|
|
148
|
+
missing.push(`api-nginx published port for container ${API_CONTAINER_PORT}`);
|
|
149
|
+
}
|
|
150
|
+
if (frontendPort === void 0) {
|
|
151
|
+
missing.push(`frontend published port for container ${FRONTEND_CONTAINER_PORT}`);
|
|
152
|
+
}
|
|
153
|
+
if (apiPort === void 0 || frontendPort === void 0) {
|
|
154
|
+
return unresolved(git.root, overridePath, missing.join("; "));
|
|
155
|
+
}
|
|
156
|
+
return {
|
|
157
|
+
kind: "resolved-worktree",
|
|
158
|
+
root: git.root,
|
|
159
|
+
overridePath,
|
|
160
|
+
apiPort,
|
|
161
|
+
frontendPort
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
function worktreeFailClosed(checkout, missingFields) {
|
|
165
|
+
throw new WorktreeConfigError(
|
|
166
|
+
`Cannot resolve ${missingFields.join(" and ")} URL(s) for this linked worktree.
|
|
167
|
+
Checkout: ${checkout.root}
|
|
168
|
+
Override: ${checkout.overridePath}
|
|
169
|
+
Missing: ${checkout.reason}
|
|
170
|
+
Re-run scripts/worktree-setup.sh . from this checkout, or pass explicit --api-url and GRAVITY_RAIL_FRONTEND_URL (or both GRAVITY_RAIL_API_URL and GRAVITY_RAIL_FRONTEND_URL) so neither URL inherits the primary checkout.`,
|
|
171
|
+
checkout.root,
|
|
172
|
+
checkout.overridePath
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
function worktreeDevUrls(apiPort, frontendPort) {
|
|
176
|
+
return {
|
|
177
|
+
apiUrl: `https://api.gravityrail.test:${apiPort}`,
|
|
178
|
+
frontendUrl: `https://app.gravityrail.test:${frontendPort}`
|
|
179
|
+
};
|
|
51
180
|
}
|
|
52
|
-
function getEnvironmentConfig(env) {
|
|
181
|
+
function getEnvironmentConfig(env, options = {}) {
|
|
53
182
|
if (env.startsWith("dev-cloud:")) {
|
|
54
183
|
const namespace = env.slice("dev-cloud:".length);
|
|
55
184
|
if (!namespace) {
|
|
56
185
|
throw new Error("dev-cloud environment requires a namespace: --env dev-cloud:NAMESPACE");
|
|
57
186
|
}
|
|
58
187
|
return {
|
|
59
|
-
apiUrl: `https://${namespace}-api.gravityrail-dev.com`,
|
|
60
|
-
frontendUrl: `https://${namespace}-app.gravityrail-dev.com`
|
|
188
|
+
apiUrl: options.apiUrl || process.env.GRAVITY_RAIL_API_URL || `https://${namespace}-api.gravityrail-dev.com`,
|
|
189
|
+
frontendUrl: options.frontendUrl || process.env.GRAVITY_RAIL_FRONTEND_URL || `https://${namespace}-app.gravityrail-dev.com`
|
|
61
190
|
};
|
|
62
191
|
}
|
|
63
192
|
const config = ENVIRONMENTS[env];
|
|
@@ -66,21 +195,35 @@ function getEnvironmentConfig(env) {
|
|
|
66
195
|
`Invalid environment: ${env}. Use 'dev', 'staging', 'prod', or 'dev-cloud:NAMESPACE'.`
|
|
67
196
|
);
|
|
68
197
|
}
|
|
69
|
-
const
|
|
70
|
-
const
|
|
71
|
-
if (
|
|
72
|
-
return {
|
|
198
|
+
const explicitApi = options.apiUrl || process.env.GRAVITY_RAIL_API_URL;
|
|
199
|
+
const explicitFrontend = options.frontendUrl || process.env.GRAVITY_RAIL_FRONTEND_URL;
|
|
200
|
+
if (env !== "dev") {
|
|
201
|
+
return {
|
|
202
|
+
apiUrl: explicitApi || config.apiUrl,
|
|
203
|
+
frontendUrl: explicitFrontend || config.frontendUrl
|
|
204
|
+
};
|
|
73
205
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
};
|
|
81
|
-
}
|
|
206
|
+
const checkout = classifyCheckout(options.startDir ?? process.cwd());
|
|
207
|
+
if (checkout.kind === "non-worktree") {
|
|
208
|
+
return {
|
|
209
|
+
apiUrl: explicitApi || config.apiUrl,
|
|
210
|
+
frontendUrl: explicitFrontend || config.frontendUrl
|
|
211
|
+
};
|
|
82
212
|
}
|
|
83
|
-
|
|
213
|
+
if (checkout.kind === "resolved-worktree") {
|
|
214
|
+
const detected = worktreeDevUrls(checkout.apiPort, checkout.frontendPort);
|
|
215
|
+
return {
|
|
216
|
+
apiUrl: explicitApi || detected.apiUrl,
|
|
217
|
+
frontendUrl: explicitFrontend || detected.frontendUrl
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
const missingFields = [];
|
|
221
|
+
if (!explicitApi) missingFields.push("API");
|
|
222
|
+
if (!explicitFrontend) missingFields.push("frontend");
|
|
223
|
+
if (missingFields.length > 0) {
|
|
224
|
+
worktreeFailClosed(checkout, missingFields);
|
|
225
|
+
}
|
|
226
|
+
return { apiUrl: explicitApi, frontendUrl: explicitFrontend };
|
|
84
227
|
}
|
|
85
228
|
|
|
86
229
|
// src/cli-utils/auth.ts
|
|
@@ -1008,6 +1151,33 @@ var AGUIClient = class {
|
|
|
1008
1151
|
this.model = "model" in options ? options.model : "manager";
|
|
1009
1152
|
this.appContext = options.appContext;
|
|
1010
1153
|
}
|
|
1154
|
+
chatRouteUrl(suffix) {
|
|
1155
|
+
const noChat = typeof this.chatId === "number" ? this.chatId <= 0 : this.chatId.length === 0;
|
|
1156
|
+
if (!this.workspaceUuid || noChat) return null;
|
|
1157
|
+
return `${this.baseUrl}/api/v2/w/${this.workspaceUuid}/chats/${this.chatId}/${suffix}`;
|
|
1158
|
+
}
|
|
1159
|
+
/**
|
|
1160
|
+
* The operator's own live inline-connect link for this chat, or null when
|
|
1161
|
+
* none is live. The `inline_connect` prompt carries no URL on purpose (it is
|
|
1162
|
+
* a bearer, and the tool result is persisted and model-visible), so the
|
|
1163
|
+
* terminal fetches it here when the operator chooses to connect.
|
|
1164
|
+
*/
|
|
1165
|
+
async fetchInlineConnectLink(appSlug) {
|
|
1166
|
+
const url = this.chatRouteUrl(`inline-connect/${encodeURIComponent(appSlug)}/link`);
|
|
1167
|
+
if (!url) return null;
|
|
1168
|
+
try {
|
|
1169
|
+
const res = await fetch(url, { headers: { Authorization: `Bearer ${this.accessToken}` } });
|
|
1170
|
+
if (!res.ok) return null;
|
|
1171
|
+
const body = await res.json();
|
|
1172
|
+
if (typeof body.connectUrl !== "string") return null;
|
|
1173
|
+
return {
|
|
1174
|
+
connectUrl: body.connectUrl,
|
|
1175
|
+
expiresInMinutes: typeof body.expiresInMinutes === "number" ? body.expiresInMinutes : 0
|
|
1176
|
+
};
|
|
1177
|
+
} catch {
|
|
1178
|
+
return null;
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1011
1181
|
inferenceStatusUrl() {
|
|
1012
1182
|
const noChat = typeof this.chatId === "number" ? this.chatId <= 0 : this.chatId.length === 0;
|
|
1013
1183
|
if (!this.workspaceUuid || noChat) return null;
|
|
@@ -1248,6 +1418,9 @@ export {
|
|
|
1248
1418
|
CALLBACK_PORT,
|
|
1249
1419
|
CALLBACK_PATH,
|
|
1250
1420
|
REDIRECT_URI,
|
|
1421
|
+
WorktreeConfigError,
|
|
1422
|
+
parsePublishedTarget,
|
|
1423
|
+
classifyCheckout,
|
|
1251
1424
|
getEnvironmentConfig,
|
|
1252
1425
|
DEFAULT_PROFILE,
|
|
1253
1426
|
CredentialsManager,
|
|
@@ -1272,4 +1445,4 @@ export {
|
|
|
1272
1445
|
getTextDelta,
|
|
1273
1446
|
getErrorMessage
|
|
1274
1447
|
};
|
|
1275
|
-
//# sourceMappingURL=chunk-
|
|
1448
|
+
//# sourceMappingURL=chunk-IGZVMTG5.js.map
|