@gravity-rail/cli 0.5.0 → 0.5.1
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 +25 -0
- package/dist/chunk-424PT5DM.js +23 -0
- package/dist/chunk-424PT5DM.js.map +1 -0
- package/dist/{chunk-C6D4FO6A.js → chunk-H62EC2CF.js} +66 -24
- package/dist/{chunk-C6D4FO6A.js.map → chunk-H62EC2CF.js.map} +1 -1
- package/dist/chunk-OPWCTOHP.js +49 -0
- package/dist/chunk-OPWCTOHP.js.map +1 -0
- package/dist/chunk-XRBVAFCB.js +47 -0
- package/dist/chunk-XRBVAFCB.js.map +1 -0
- package/dist/{auth-DBspA1Dh.d.ts → cli-client-context-9KRUt6O-.d.ts} +13 -1
- package/dist/cli-utils.d.ts +26 -5
- package/dist/cli-utils.js +2 -1
- package/dist/errors-4MAJGD3C.js +12 -0
- package/dist/errors-4MAJGD3C.js.map +1 -0
- package/dist/errors-DJILV5RW.js +4 -0
- package/dist/errors-DJILV5RW.js.map +1 -0
- package/dist/gr.js +946 -200
- package/dist/gr.js.map +1 -1
- package/dist/lib.d.ts +25 -3
- package/dist/lib.js +752 -165
- package/dist/lib.js.map +1 -1
- package/package.json +2 -2
package/dist/gr.js
CHANGED
|
@@ -1,56 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { CLIError, handleError, AuthRequiredError } from './chunk-XRBVAFCB.js';
|
|
3
|
+
import fs, { readFileSync, existsSync, mkdirSync, writeFileSync, chmodSync, unlinkSync, createWriteStream } from 'fs';
|
|
4
|
+
import path3, { join, resolve, dirname } from 'path';
|
|
2
5
|
import { createServer } from 'http';
|
|
3
6
|
import { randomBytes, createHash } from 'crypto';
|
|
4
|
-
import { readFileSync, existsSync, mkdirSync, writeFileSync, chmodSync, unlinkSync, createWriteStream } from 'fs';
|
|
5
|
-
import { join } from 'path';
|
|
6
7
|
import { execSync, execFile, exec, spawn, spawnSync } from 'child_process';
|
|
7
8
|
import * as readline from 'readline';
|
|
8
|
-
import
|
|
9
|
+
import readline__default from 'readline';
|
|
10
|
+
import os, { homedir } from 'os';
|
|
9
11
|
import { EventEmitter } from 'events';
|
|
10
12
|
|
|
11
|
-
// src/lib/errors.ts
|
|
12
|
-
var CLIError = class extends Error {
|
|
13
|
-
constructor(message, exitCode = 1) {
|
|
14
|
-
super(message);
|
|
15
|
-
this.exitCode = exitCode;
|
|
16
|
-
this.name = "CLIError";
|
|
17
|
-
}
|
|
18
|
-
};
|
|
19
|
-
var AuthRequiredError = class extends CLIError {
|
|
20
|
-
constructor(message = "Not authenticated. Run `gr login` or set GRAVITY_RAIL_API_KEY.") {
|
|
21
|
-
super(message, 2);
|
|
22
|
-
this.name = "AuthRequiredError";
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
function handleError(err) {
|
|
26
|
-
if (err instanceof CLIError) {
|
|
27
|
-
process.stderr.write(`Error: ${err.message}
|
|
28
|
-
`);
|
|
29
|
-
process.exit(err.exitCode);
|
|
30
|
-
}
|
|
31
|
-
if (err instanceof Error) {
|
|
32
|
-
process.stderr.write(`Error: ${err.message}
|
|
33
|
-
`);
|
|
34
|
-
if ("errors" in err && Array.isArray(err.errors)) {
|
|
35
|
-
for (const e of err.errors) {
|
|
36
|
-
process.stderr.write(` - ${e}
|
|
37
|
-
`);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
if ("statusCode" in err && err.statusCode === 401) {
|
|
41
|
-
process.stderr.write(
|
|
42
|
-
`
|
|
43
|
-
Hint: Run "gr login" to authenticate, or set GRAVITY_RAIL_API_KEY for non-interactive use.
|
|
44
|
-
`
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
process.exit(1);
|
|
48
|
-
}
|
|
49
|
-
process.stderr.write(`Error: ${String(err)}
|
|
50
|
-
`);
|
|
51
|
-
process.exit(1);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
13
|
// src/lib/pagination.ts
|
|
55
14
|
function isListResult(data) {
|
|
56
15
|
if (!data || typeof data !== "object" || Array.isArray(data)) return false;
|
|
@@ -174,7 +133,7 @@ var COMMON_ARGS = [
|
|
|
174
133
|
{ name: "uuid", description: "Entity UUID" }
|
|
175
134
|
];
|
|
176
135
|
function parseArgs(defs, argv) {
|
|
177
|
-
const allDefs = [...
|
|
136
|
+
const allDefs = [...defs, ...COMMON_ARGS];
|
|
178
137
|
const result2 = {};
|
|
179
138
|
for (const def of allDefs) {
|
|
180
139
|
if (def.default !== void 0) {
|
|
@@ -201,8 +160,7 @@ function parseArgs(defs, argv) {
|
|
|
201
160
|
const def = findDef(allDefs, flagPart);
|
|
202
161
|
if (!def) {
|
|
203
162
|
if ((flagPart.startsWith("--") || flagPart.startsWith("-")) && !KNOWN_GLOBAL_FLAGS.has(flagPart)) {
|
|
204
|
-
|
|
205
|
-
`);
|
|
163
|
+
throw new CLIError(`Unknown flag "${flagPart}"`);
|
|
206
164
|
}
|
|
207
165
|
continue;
|
|
208
166
|
}
|
|
@@ -217,9 +175,11 @@ function parseArgs(defs, argv) {
|
|
|
217
175
|
}
|
|
218
176
|
}
|
|
219
177
|
for (const def of allDefs) {
|
|
220
|
-
if (def.required
|
|
221
|
-
|
|
222
|
-
|
|
178
|
+
if (!def.required) continue;
|
|
179
|
+
if (result2[def.name] !== void 0) continue;
|
|
180
|
+
const aliasNames = [def.alias, ...def.aliases ?? []].filter(Boolean);
|
|
181
|
+
if (aliasNames.some((a) => result2[a] !== void 0)) continue;
|
|
182
|
+
throw new CLIError(`Missing required argument: --${def.name}`);
|
|
223
183
|
}
|
|
224
184
|
return result2;
|
|
225
185
|
}
|
|
@@ -2392,7 +2352,7 @@ function parsePhoneNumber4() {
|
|
|
2392
2352
|
return withMetadataArgument(parsePhoneNumber3, arguments);
|
|
2393
2353
|
}
|
|
2394
2354
|
|
|
2395
|
-
// ../sdk/dist/chunk-
|
|
2355
|
+
// ../sdk/dist/chunk-XRKRVY46.js
|
|
2396
2356
|
function getCookieValue(name) {
|
|
2397
2357
|
if (typeof document === "undefined") return void 0;
|
|
2398
2358
|
const cookies = document.cookie.split(";");
|
|
@@ -2800,7 +2760,7 @@ ${formattedErrors}`, response.status);
|
|
|
2800
2760
|
* Fetch from root path (not prefixed with /api/v2/)
|
|
2801
2761
|
* Used for OAuth and other non-API endpoints
|
|
2802
2762
|
*/
|
|
2803
|
-
async fetchRoot(
|
|
2763
|
+
async fetchRoot(path4, options) {
|
|
2804
2764
|
try {
|
|
2805
2765
|
const headers2 = new Headers({
|
|
2806
2766
|
...options?.body instanceof FormData ? {} : { "Content-Type": "application/json" },
|
|
@@ -2820,7 +2780,7 @@ ${formattedErrors}`, response.status);
|
|
|
2820
2780
|
if (this.orgToken) {
|
|
2821
2781
|
headers2.set("X-Org-Token", this.orgToken);
|
|
2822
2782
|
}
|
|
2823
|
-
const fullUrl = `${this.baseUrl}/${
|
|
2783
|
+
const fullUrl = `${this.baseUrl}/${path4}`;
|
|
2824
2784
|
const response = await this.fetchFunction(fullUrl, {
|
|
2825
2785
|
...options,
|
|
2826
2786
|
...this.apiKey ? {} : { credentials: "include" },
|
|
@@ -7034,22 +6994,22 @@ var OAuthConnectionsApi = class extends BaseApiClient {
|
|
|
7034
6994
|
* Get the connection status for an OAuth provider
|
|
7035
6995
|
*/
|
|
7036
6996
|
async getConnectionStatus(providerSlug) {
|
|
7037
|
-
const
|
|
7038
|
-
return this.fetchRoot(
|
|
6997
|
+
const path4 = getOAuthPath(providerSlug, "status");
|
|
6998
|
+
return this.fetchRoot(path4);
|
|
7039
6999
|
}
|
|
7040
7000
|
/**
|
|
7041
7001
|
* Get the authorization URL to start OAuth flow
|
|
7042
7002
|
*/
|
|
7043
7003
|
async getAuthorizationUrl(providerSlug) {
|
|
7044
|
-
const
|
|
7045
|
-
return this.fetchRoot(
|
|
7004
|
+
const path4 = getOAuthPath(providerSlug, "authorize");
|
|
7005
|
+
return this.fetchRoot(path4);
|
|
7046
7006
|
}
|
|
7047
7007
|
/**
|
|
7048
7008
|
* Disconnect an OAuth provider
|
|
7049
7009
|
*/
|
|
7050
7010
|
async disconnect(providerSlug) {
|
|
7051
|
-
const
|
|
7052
|
-
return this.fetchRoot(
|
|
7011
|
+
const path4 = getOAuthPath(providerSlug, "disconnect");
|
|
7012
|
+
return this.fetchRoot(path4, { method: "DELETE" });
|
|
7053
7013
|
}
|
|
7054
7014
|
};
|
|
7055
7015
|
var OAuthApi = class extends BaseApiClient {
|
|
@@ -12990,11 +12950,87 @@ var ApiClient = class extends GravityRailClient {
|
|
|
12990
12950
|
// ignore X-Org-Token entirely, so propagating an org token to adminApi is
|
|
12991
12951
|
// dead code that would only obscure auth flow if admin route logic changes.
|
|
12992
12952
|
};
|
|
12993
|
-
|
|
12994
|
-
|
|
12953
|
+
var ENVIRONMENTS = {
|
|
12954
|
+
dev: {
|
|
12955
|
+
apiUrl: "https://api.gravityrail.test:5054",
|
|
12956
|
+
frontendUrl: "https://app.gravityrail.test:3000"
|
|
12957
|
+
},
|
|
12958
|
+
prod: {
|
|
12959
|
+
apiUrl: "https://api.gravityrail.com",
|
|
12960
|
+
frontendUrl: "https://app.gravityrail.com"
|
|
12961
|
+
},
|
|
12962
|
+
staging: {
|
|
12963
|
+
apiUrl: "https://api.gr-staging.com",
|
|
12964
|
+
frontendUrl: "https://app.gr-staging.com"
|
|
12965
|
+
}
|
|
12966
|
+
};
|
|
12995
12967
|
var CALLBACK_PORT = 31337;
|
|
12996
12968
|
var CALLBACK_PATH = "/oauth/callback";
|
|
12997
12969
|
var REDIRECT_URI = `http://localhost:${CALLBACK_PORT}${CALLBACK_PATH}`;
|
|
12970
|
+
function detectWorktreePorts() {
|
|
12971
|
+
let dir = resolve(process.cwd());
|
|
12972
|
+
let overridePath;
|
|
12973
|
+
while (true) {
|
|
12974
|
+
const candidate = join(dir, "docker-compose.override.yml");
|
|
12975
|
+
if (existsSync(candidate)) {
|
|
12976
|
+
overridePath = candidate;
|
|
12977
|
+
break;
|
|
12978
|
+
}
|
|
12979
|
+
const parent = dirname(dir);
|
|
12980
|
+
if (parent === dir) break;
|
|
12981
|
+
dir = parent;
|
|
12982
|
+
}
|
|
12983
|
+
if (!overridePath) return {};
|
|
12984
|
+
let content;
|
|
12985
|
+
try {
|
|
12986
|
+
content = readFileSync(overridePath, "utf-8");
|
|
12987
|
+
} catch {
|
|
12988
|
+
return {};
|
|
12989
|
+
}
|
|
12990
|
+
if (!content.includes("worktree-setup.sh")) return {};
|
|
12991
|
+
let apiPort;
|
|
12992
|
+
let frontendPort;
|
|
12993
|
+
for (const match of content.matchAll(/-\s*'(\d+):(\d+)'/g)) {
|
|
12994
|
+
const host = parseInt(match[1], 10);
|
|
12995
|
+
const container = parseInt(match[2], 10);
|
|
12996
|
+
if (container === 5050) apiPort = host;
|
|
12997
|
+
if (container === 3e3) frontendPort = host;
|
|
12998
|
+
}
|
|
12999
|
+
return { apiPort, frontendPort };
|
|
13000
|
+
}
|
|
13001
|
+
function getEnvironmentConfig(env) {
|
|
13002
|
+
if (env.startsWith("dev-cloud:")) {
|
|
13003
|
+
const namespace = env.slice("dev-cloud:".length);
|
|
13004
|
+
if (!namespace) {
|
|
13005
|
+
throw new Error("dev-cloud environment requires a namespace: --env dev-cloud:NAMESPACE");
|
|
13006
|
+
}
|
|
13007
|
+
return {
|
|
13008
|
+
apiUrl: `https://${namespace}-api.gravityrail-dev.com`,
|
|
13009
|
+
frontendUrl: `https://${namespace}-app.gravityrail-dev.com`
|
|
13010
|
+
};
|
|
13011
|
+
}
|
|
13012
|
+
const config = ENVIRONMENTS[env];
|
|
13013
|
+
if (!config) {
|
|
13014
|
+
throw new Error(
|
|
13015
|
+
`Invalid environment: ${env}. Use 'dev', 'staging', 'prod', or 'dev-cloud:NAMESPACE'.`
|
|
13016
|
+
);
|
|
13017
|
+
}
|
|
13018
|
+
const apiUrl = process.env.GRAVITY_RAIL_API_URL || config.apiUrl;
|
|
13019
|
+
const frontendUrl = process.env.GRAVITY_RAIL_FRONTEND_URL || config.frontendUrl;
|
|
13020
|
+
if (apiUrl !== config.apiUrl || frontendUrl !== config.frontendUrl) {
|
|
13021
|
+
return { apiUrl, frontendUrl };
|
|
13022
|
+
}
|
|
13023
|
+
if (env === "dev") {
|
|
13024
|
+
const ports = detectWorktreePorts();
|
|
13025
|
+
if (ports.apiPort || ports.frontendPort) {
|
|
13026
|
+
return {
|
|
13027
|
+
apiUrl: ports.apiPort ? `https://api.gravityrail.test:${ports.apiPort}` : config.apiUrl,
|
|
13028
|
+
frontendUrl: ports.frontendPort ? `https://app.gravityrail.test:${ports.frontendPort}` : config.frontendUrl
|
|
13029
|
+
};
|
|
13030
|
+
}
|
|
13031
|
+
}
|
|
13032
|
+
return config;
|
|
13033
|
+
}
|
|
12998
13034
|
var CredentialsManager = class {
|
|
12999
13035
|
cacheDir;
|
|
13000
13036
|
apiUrl;
|
|
@@ -13124,7 +13160,7 @@ function openBrowser(url) {
|
|
|
13124
13160
|
});
|
|
13125
13161
|
}
|
|
13126
13162
|
function startCallbackServer() {
|
|
13127
|
-
return new Promise((
|
|
13163
|
+
return new Promise((resolve2, reject) => {
|
|
13128
13164
|
let authCode = null;
|
|
13129
13165
|
let error = null;
|
|
13130
13166
|
let receivedState = null;
|
|
@@ -13166,7 +13202,7 @@ function startCallbackServer() {
|
|
|
13166
13202
|
</html>
|
|
13167
13203
|
`);
|
|
13168
13204
|
server.close();
|
|
13169
|
-
|
|
13205
|
+
resolve2({ code: authCode, state: receivedState });
|
|
13170
13206
|
} else {
|
|
13171
13207
|
res.writeHead(400);
|
|
13172
13208
|
res.end("Missing code parameter");
|
|
@@ -13358,7 +13394,7 @@ async function selectFromList(items, options = {}) {
|
|
|
13358
13394
|
return { item: null, isNew: false };
|
|
13359
13395
|
}
|
|
13360
13396
|
const displayItems = allowNew ? [NEW_ITEM_MARKER, ...items] : items;
|
|
13361
|
-
return new Promise((
|
|
13397
|
+
return new Promise((resolve2) => {
|
|
13362
13398
|
let selectedIndex = allowNew ? 1 : 0;
|
|
13363
13399
|
let filterText = "";
|
|
13364
13400
|
const getFilteredItems = () => {
|
|
@@ -13441,16 +13477,16 @@ async function selectFromList(items, options = {}) {
|
|
|
13441
13477
|
if (filtered.length > 0 && selectedIndex < filtered.length) {
|
|
13442
13478
|
const selected = filtered[selectedIndex];
|
|
13443
13479
|
if (selected === NEW_ITEM_MARKER) {
|
|
13444
|
-
|
|
13480
|
+
resolve2({ item: null, isNew: true });
|
|
13445
13481
|
} else {
|
|
13446
|
-
|
|
13482
|
+
resolve2({ item: selected, isNew: false });
|
|
13447
13483
|
}
|
|
13448
13484
|
} else {
|
|
13449
|
-
|
|
13485
|
+
resolve2({ item: null, isNew: false });
|
|
13450
13486
|
}
|
|
13451
13487
|
} else if (key.name === "escape" || key.ctrl && key.name === "c") {
|
|
13452
13488
|
cleanup();
|
|
13453
|
-
|
|
13489
|
+
resolve2({ item: null, isNew: false });
|
|
13454
13490
|
} else if (key.name === "backspace") {
|
|
13455
13491
|
filterText = filterText.slice(0, -1);
|
|
13456
13492
|
selectedIndex = allowNew ? 0 : 0;
|
|
@@ -13560,29 +13596,82 @@ function clearScreen() {
|
|
|
13560
13596
|
// src/cli-utils/agui.ts
|
|
13561
13597
|
var AGUIClient = class {
|
|
13562
13598
|
endpointUrl;
|
|
13599
|
+
baseUrl;
|
|
13600
|
+
workspaceUuid;
|
|
13601
|
+
chatId;
|
|
13563
13602
|
accessToken;
|
|
13564
13603
|
debug;
|
|
13565
13604
|
model;
|
|
13605
|
+
appContext;
|
|
13566
13606
|
constructor(options) {
|
|
13607
|
+
this.baseUrl = options.baseUrl;
|
|
13608
|
+
this.workspaceUuid = options.workspaceUuid ?? "";
|
|
13609
|
+
this.chatId = options.chatId ?? 0;
|
|
13610
|
+
if (!options.endpointUrl && (!options.workspaceUuid || options.chatId === void 0)) {
|
|
13611
|
+
throw new Error("AGUIClient requires workspaceUuid and chatId, or endpointUrl");
|
|
13612
|
+
}
|
|
13567
13613
|
this.endpointUrl = options.endpointUrl ?? `${options.baseUrl}/api/v2/w/${options.workspaceUuid}/web-chat/${options.chatId}/chat/agui`;
|
|
13568
13614
|
this.accessToken = options.accessToken;
|
|
13569
13615
|
this.debug = options.debug ?? false;
|
|
13570
13616
|
this.model = "model" in options ? options.model : "manager";
|
|
13617
|
+
this.appContext = options.appContext;
|
|
13618
|
+
}
|
|
13619
|
+
inferenceStatusUrl() {
|
|
13620
|
+
if (!this.workspaceUuid || this.chatId <= 0) return null;
|
|
13621
|
+
return `${this.baseUrl}/api/v2/w/${this.workspaceUuid}/chats/${this.chatId}/inference/status`;
|
|
13622
|
+
}
|
|
13623
|
+
async waitForInferenceInactive(maxWaitMs = 6e4) {
|
|
13624
|
+
const url = this.inferenceStatusUrl();
|
|
13625
|
+
if (!url) return false;
|
|
13626
|
+
const deadline = Date.now() + maxWaitMs;
|
|
13627
|
+
while (Date.now() < deadline) {
|
|
13628
|
+
try {
|
|
13629
|
+
const res = await fetch(url, {
|
|
13630
|
+
headers: { Authorization: `Bearer ${this.accessToken}` }
|
|
13631
|
+
});
|
|
13632
|
+
if (res.ok) {
|
|
13633
|
+
const body = await res.json();
|
|
13634
|
+
if (!body.active) return true;
|
|
13635
|
+
}
|
|
13636
|
+
} catch (err) {
|
|
13637
|
+
if (this.debug) {
|
|
13638
|
+
console.log(
|
|
13639
|
+
`[DEBUG] waitForInferenceInactive fetch failed: ${err instanceof Error ? err.message : String(err)}`
|
|
13640
|
+
);
|
|
13641
|
+
}
|
|
13642
|
+
}
|
|
13643
|
+
await new Promise((r) => setTimeout(r, 2e3));
|
|
13644
|
+
}
|
|
13645
|
+
return false;
|
|
13571
13646
|
}
|
|
13572
13647
|
/**
|
|
13573
13648
|
* Send messages and stream the response
|
|
13574
13649
|
*/
|
|
13575
13650
|
async sendMessage(messages, onEvent, options) {
|
|
13651
|
+
return this.streamRequest({ messages }, onEvent, options);
|
|
13652
|
+
}
|
|
13653
|
+
/**
|
|
13654
|
+
* Stream an AG-UI request (initial messages or HITL resume).
|
|
13655
|
+
*/
|
|
13656
|
+
async streamRequest(request, onEvent, options) {
|
|
13576
13657
|
const body = JSON.stringify({
|
|
13577
13658
|
...this.model ? { model: this.model } : {},
|
|
13578
|
-
messages,
|
|
13659
|
+
messages: request.messages ?? [],
|
|
13660
|
+
...this.appContext ? { appContext: this.appContext } : {},
|
|
13661
|
+
...request.resumeValue !== void 0 ? { resumeValue: request.resumeValue } : {},
|
|
13662
|
+
...request.resumeInterruptId ? { resumeInterruptId: request.resumeInterruptId } : {},
|
|
13579
13663
|
...options?.autoApprove ? { context: { autoApprove: true } } : {}
|
|
13580
13664
|
});
|
|
13581
13665
|
if (this.debug) {
|
|
13582
13666
|
console.log(`
|
|
13583
13667
|
[DEBUG] POST ${this.endpointUrl}`);
|
|
13584
|
-
console.log(
|
|
13668
|
+
console.log(
|
|
13669
|
+
`[DEBUG] Body: ${request.resumeValue !== void 0 ? "resume" : `${(request.messages ?? []).length} message(s)`}`
|
|
13670
|
+
);
|
|
13585
13671
|
}
|
|
13672
|
+
await this.postStream(body, onEvent, options);
|
|
13673
|
+
}
|
|
13674
|
+
async postStream(body, onEvent, options, conflictRetries = 0) {
|
|
13586
13675
|
const response = await fetch(this.endpointUrl, {
|
|
13587
13676
|
method: "POST",
|
|
13588
13677
|
headers: {
|
|
@@ -13595,12 +13684,21 @@ var AGUIClient = class {
|
|
|
13595
13684
|
if (this.debug) {
|
|
13596
13685
|
console.log(`[DEBUG] Response status: ${response.status}`);
|
|
13597
13686
|
}
|
|
13687
|
+
if (response.status === 409 && conflictRetries < 3) {
|
|
13688
|
+
const ready = await this.waitForInferenceInactive();
|
|
13689
|
+
if (ready) {
|
|
13690
|
+
await this.postStream(body, onEvent, options, conflictRetries + 1);
|
|
13691
|
+
return;
|
|
13692
|
+
}
|
|
13693
|
+
}
|
|
13598
13694
|
if (!response.ok) {
|
|
13599
13695
|
const errorText = await response.text().catch(() => "");
|
|
13600
13696
|
if (this.debug) {
|
|
13601
13697
|
console.log(`[DEBUG] Error response: ${errorText}`);
|
|
13602
13698
|
}
|
|
13603
|
-
throw new Error(
|
|
13699
|
+
throw new Error(
|
|
13700
|
+
`Chat request failed: ${response.status}${errorText ? `: ${errorText.slice(0, 200)}` : ""}`
|
|
13701
|
+
);
|
|
13604
13702
|
}
|
|
13605
13703
|
const reader = response.body.getReader();
|
|
13606
13704
|
const decoder = new TextDecoder();
|
|
@@ -13873,12 +13971,14 @@ function requireAuth(ctx) {
|
|
|
13873
13971
|
return ctx;
|
|
13874
13972
|
}
|
|
13875
13973
|
function createPublicContext(opts) {
|
|
13876
|
-
const
|
|
13877
|
-
const
|
|
13878
|
-
|
|
13879
|
-
|
|
13974
|
+
const env = opts.env ?? "prod";
|
|
13975
|
+
const envConfig = opts.apiUrl ? {
|
|
13976
|
+
apiUrl: opts.apiUrl,
|
|
13977
|
+
frontendUrl: opts.frontendUrl || process.env.GRAVITY_RAIL_FRONTEND_URL || "https://app.gravityrail.com"
|
|
13978
|
+
} : getEnvironmentConfig(env);
|
|
13979
|
+
const credentialsManager = new CredentialsManager(homedir(), envConfig.apiUrl);
|
|
13880
13980
|
return {
|
|
13881
|
-
env
|
|
13981
|
+
env,
|
|
13882
13982
|
envConfig,
|
|
13883
13983
|
outputFormat: detectOutputFormat(opts.outputFormat),
|
|
13884
13984
|
verbose: opts.verbose ?? false,
|
|
@@ -13927,32 +14027,48 @@ async function readJsonInput(args) {
|
|
|
13927
14027
|
throw new CLIError(`Cannot read file: ${filePath}`);
|
|
13928
14028
|
}
|
|
13929
14029
|
}
|
|
13930
|
-
if (
|
|
13931
|
-
return
|
|
14030
|
+
if (process.stdin.isTTY === true) {
|
|
14031
|
+
return void 0;
|
|
13932
14032
|
}
|
|
13933
|
-
return
|
|
14033
|
+
return readStdinIfPiped();
|
|
13934
14034
|
}
|
|
13935
|
-
function
|
|
13936
|
-
return new Promise((
|
|
14035
|
+
function readStdinIfPiped() {
|
|
14036
|
+
return new Promise((resolve2, reject) => {
|
|
13937
14037
|
let data = "";
|
|
13938
|
-
|
|
13939
|
-
|
|
13940
|
-
|
|
13941
|
-
|
|
13942
|
-
|
|
14038
|
+
let settled = false;
|
|
14039
|
+
const finish = (value) => {
|
|
14040
|
+
if (settled) return;
|
|
14041
|
+
settled = true;
|
|
14042
|
+
resolve2(value);
|
|
14043
|
+
};
|
|
14044
|
+
const flush = () => {
|
|
13943
14045
|
if (!data.trim()) {
|
|
13944
|
-
|
|
14046
|
+
finish(void 0);
|
|
13945
14047
|
return;
|
|
13946
14048
|
}
|
|
13947
14049
|
try {
|
|
13948
|
-
|
|
14050
|
+
finish(JSON.parse(data));
|
|
13949
14051
|
} catch {
|
|
13950
14052
|
reject(new CLIError("Invalid JSON from stdin"));
|
|
13951
14053
|
}
|
|
14054
|
+
};
|
|
14055
|
+
process.stdin.setEncoding("utf-8");
|
|
14056
|
+
process.stdin.on("data", (chunk) => {
|
|
14057
|
+
data += chunk;
|
|
13952
14058
|
});
|
|
14059
|
+
process.stdin.on("end", flush);
|
|
13953
14060
|
process.stdin.on("error", (err) => {
|
|
13954
14061
|
reject(new CLIError(`Error reading stdin: ${err.message}`));
|
|
13955
14062
|
});
|
|
14063
|
+
process.stdin.resume();
|
|
14064
|
+
setImmediate(() => {
|
|
14065
|
+
if (data) return;
|
|
14066
|
+
if (process.stdin.readableEnded) {
|
|
14067
|
+
flush();
|
|
14068
|
+
return;
|
|
14069
|
+
}
|
|
14070
|
+
finish(void 0);
|
|
14071
|
+
});
|
|
13956
14072
|
});
|
|
13957
14073
|
}
|
|
13958
14074
|
|
|
@@ -14358,6 +14474,17 @@ function requireNumber(args, key) {
|
|
|
14358
14474
|
if (isNaN(num)) throw new Error(`Argument --${key} must be a number`);
|
|
14359
14475
|
return num;
|
|
14360
14476
|
}
|
|
14477
|
+
function requireNumberFrom(args, ...keys) {
|
|
14478
|
+
for (const key of keys) {
|
|
14479
|
+
const val = args[key];
|
|
14480
|
+
if (typeof val === "string") {
|
|
14481
|
+
const num = Number(val);
|
|
14482
|
+
if (!isNaN(num)) return num;
|
|
14483
|
+
}
|
|
14484
|
+
}
|
|
14485
|
+
const hint = keys.map((k) => `--${k}`).join(" or ");
|
|
14486
|
+
throw new Error(`Missing required argument: ${hint}`);
|
|
14487
|
+
}
|
|
14361
14488
|
|
|
14362
14489
|
// src/domains/agent-tasks.ts
|
|
14363
14490
|
var LIST_COLUMNS = [
|
|
@@ -14518,6 +14645,7 @@ var agentsDomain = {
|
|
|
14518
14645
|
var COLUMNS2 = [
|
|
14519
14646
|
COL.id(),
|
|
14520
14647
|
COL.uuid(),
|
|
14648
|
+
{ header: "TYPE", getValue: (item) => item.chatType ?? item.chat_type ?? "" },
|
|
14521
14649
|
{ header: "MEMBER", getValue: (item) => item.member_name ?? String(item.member_id ?? "") },
|
|
14522
14650
|
COL.status(),
|
|
14523
14651
|
{ header: "CHANNEL", getValue: (item) => item.channel ?? "" },
|
|
@@ -14569,7 +14697,11 @@ var chatsDomain = {
|
|
|
14569
14697
|
isFlag: true
|
|
14570
14698
|
},
|
|
14571
14699
|
{ name: "is-test", description: "Show only test chats", isFlag: true },
|
|
14572
|
-
{ name: "member-id", description: "Filter by member ID" }
|
|
14700
|
+
{ name: "member-id", description: "Filter by member ID" },
|
|
14701
|
+
{
|
|
14702
|
+
name: "chat-type",
|
|
14703
|
+
description: "Filter by chat type (assignment, manager, developer, member, contributor). Default: assignment"
|
|
14704
|
+
}
|
|
14573
14705
|
]
|
|
14574
14706
|
}),
|
|
14575
14707
|
// Messages
|
|
@@ -14578,7 +14710,7 @@ var chatsDomain = {
|
|
|
14578
14710
|
description: "List messages for a chat. Use --limit/--offset to page; the API caps a single request at 100. Tool-call shell rows that the API filter excludes (parent_message_id IS NOT NULL or parent_call_id IS NOT NULL) are still hidden \u2014 use `gr chats inspect --id <chat>` for the full debug view.",
|
|
14579
14711
|
requiresWorkspace: true,
|
|
14580
14712
|
args: [
|
|
14581
|
-
{ name: "chat-id", description: "Chat ID", required: true },
|
|
14713
|
+
{ name: "chat-id", aliases: ["id"], description: "Chat ID", required: true },
|
|
14582
14714
|
{
|
|
14583
14715
|
name: "limit",
|
|
14584
14716
|
description: "Max messages to return (1-100, default 100)"
|
|
@@ -14601,7 +14733,7 @@ var chatsDomain = {
|
|
|
14601
14733
|
}
|
|
14602
14734
|
const result2 = await requireAuth(ctx).apiClient.getChatMessages(
|
|
14603
14735
|
workspace,
|
|
14604
|
-
|
|
14736
|
+
requireNumberFrom(args, "chat-id", "id"),
|
|
14605
14737
|
limit,
|
|
14606
14738
|
offset
|
|
14607
14739
|
);
|
|
@@ -14614,7 +14746,7 @@ var chatsDomain = {
|
|
|
14614
14746
|
mutates: true,
|
|
14615
14747
|
requiresWorkspace: true,
|
|
14616
14748
|
args: [
|
|
14617
|
-
{ name: "chat-id", description: "Chat ID", required: true },
|
|
14749
|
+
{ name: "chat-id", aliases: ["id"], description: "Chat ID", required: true },
|
|
14618
14750
|
{ name: "stream", description: "Stream the AI response via AG-UI", isFlag: true }
|
|
14619
14751
|
],
|
|
14620
14752
|
handler: async ({ ctx, workspace, args, data }) => {
|
|
@@ -14623,7 +14755,7 @@ var chatsDomain = {
|
|
|
14623
14755
|
throw new Error(
|
|
14624
14756
|
`Message content is required (pass --data '"your message"' or --data '{"content":"your message"}')`
|
|
14625
14757
|
);
|
|
14626
|
-
const chatId =
|
|
14758
|
+
const chatId = requireNumberFrom(args, "chat-id", "id");
|
|
14627
14759
|
if (args.stream) {
|
|
14628
14760
|
const aguiClient = new AGUIClient({
|
|
14629
14761
|
baseUrl: ctx.envConfig.apiUrl,
|
|
@@ -14656,11 +14788,11 @@ var chatsDomain = {
|
|
|
14656
14788
|
description: "Send an assistant message to a chat (does not trigger AI inference)",
|
|
14657
14789
|
mutates: true,
|
|
14658
14790
|
requiresWorkspace: true,
|
|
14659
|
-
args: [{ name: "chat-id", description: "Chat ID", required: true }],
|
|
14791
|
+
args: [{ name: "chat-id", aliases: ["id"], description: "Chat ID", required: true }],
|
|
14660
14792
|
handler: async ({ ctx, workspace, args, data }) => {
|
|
14661
14793
|
const result2 = await requireAuth(ctx).apiClient.sendAssistantMessage(
|
|
14662
14794
|
workspace,
|
|
14663
|
-
|
|
14795
|
+
requireNumberFrom(args, "chat-id", "id"),
|
|
14664
14796
|
data
|
|
14665
14797
|
);
|
|
14666
14798
|
outputSingle(result2, ctx, MESSAGE_COLUMNS2);
|
|
@@ -16723,10 +16855,20 @@ var eventRulesCrudNoList = crudCommands({
|
|
|
16723
16855
|
' "eventType":"journey_step:execute","actionType":"member:label",',
|
|
16724
16856
|
` "actionParams":{"addLabelIds":["<label-uuid>"]},"journeyStepId":<step-id>}'`,
|
|
16725
16857
|
"",
|
|
16858
|
+
"Scheduled outbound (routine \u2192 chat \u2192 voice/SMS):",
|
|
16859
|
+
`gr events rules create -w $WID --allow-write --data '{"name":"AM voice check-in",`,
|
|
16860
|
+
' "objectType":"routine","eventType":"routine:triggered","actionType":"chat:create",',
|
|
16861
|
+
' "routineId":<routine-id>,',
|
|
16862
|
+
' "actionParams":{"workflowId":<workflow-id>,"channel":"phone-voice",',
|
|
16863
|
+
` "phoneNumberId":<workspace-phone-id>}}'`,
|
|
16864
|
+
"",
|
|
16865
|
+
' PM SMS variant: use "channel":"phone-sms" and a second rule on the same routineId.',
|
|
16866
|
+
" Prereq: org phone assigned + gr phone-numbers link; workflow published with start task.",
|
|
16867
|
+
"",
|
|
16726
16868
|
"Required: objectType, eventType, actionType",
|
|
16727
|
-
"Optional: name, actionParams, conditions (CEL), journeyStepId, delay (sec), order",
|
|
16869
|
+
"Optional: name, actionParams, conditions (CEL), journeyStepId, routineId, delay (sec), order",
|
|
16728
16870
|
"",
|
|
16729
|
-
"Common eventTypes: journey_step:execute, journey:enrolled,
|
|
16871
|
+
"Common eventTypes: journey_step:execute, journey:enrolled, routine:triggered,",
|
|
16730
16872
|
" task:created, chat:created, data_record:created, member:created",
|
|
16731
16873
|
"",
|
|
16732
16874
|
"Common actionTypes: member:label, member:notify, webhook:send, email:send,",
|
|
@@ -19009,7 +19151,7 @@ var conciergeDomain = {
|
|
|
19009
19151
|
path: ["concierge", "messages"],
|
|
19010
19152
|
description: "Show messages for a concierge chat",
|
|
19011
19153
|
requiresWorkspace: false,
|
|
19012
|
-
args: [{ name: "id", description: "Chat ID", required: true }],
|
|
19154
|
+
args: [{ name: "id", aliases: ["chat-id"], description: "Chat ID", required: true }],
|
|
19013
19155
|
handler: async ({ ctx, args }) => {
|
|
19014
19156
|
const auth = requireAuth(ctx);
|
|
19015
19157
|
const chatId = Number(args.id);
|
|
@@ -19642,15 +19784,15 @@ var DOMAIN_UUID_ARG = {
|
|
|
19642
19784
|
function pluralize(count, singular, plural = `${singular}s`) {
|
|
19643
19785
|
return count === 1 ? singular : plural;
|
|
19644
19786
|
}
|
|
19645
|
-
function emailVerifyCommand(
|
|
19787
|
+
function emailVerifyCommand(path4, description) {
|
|
19646
19788
|
return {
|
|
19647
|
-
path,
|
|
19789
|
+
path: path4,
|
|
19648
19790
|
description,
|
|
19649
19791
|
mutates: true,
|
|
19650
19792
|
requiresWorkspace: false,
|
|
19651
19793
|
args: [ORG_UUID_ARG, DOMAIN_UUID_ARG],
|
|
19652
19794
|
examples: [
|
|
19653
|
-
`yarn gr ${
|
|
19795
|
+
`yarn gr ${path4.join(" ")} --org-uuid $ORG_UUID --domain-uuid $DOMAIN_UUID --allow-write`
|
|
19654
19796
|
],
|
|
19655
19797
|
handler: async (hctx) => {
|
|
19656
19798
|
const result2 = await requireAuth(hctx.ctx).apiClient.verifyOrgDomainEmail(
|
|
@@ -19661,15 +19803,15 @@ function emailVerifyCommand(path, description) {
|
|
|
19661
19803
|
}
|
|
19662
19804
|
};
|
|
19663
19805
|
}
|
|
19664
|
-
function siteVerifyCommand(
|
|
19806
|
+
function siteVerifyCommand(path4, description) {
|
|
19665
19807
|
return {
|
|
19666
|
-
path,
|
|
19808
|
+
path: path4,
|
|
19667
19809
|
description,
|
|
19668
19810
|
mutates: true,
|
|
19669
19811
|
requiresWorkspace: false,
|
|
19670
19812
|
args: [ORG_UUID_ARG, DOMAIN_UUID_ARG],
|
|
19671
19813
|
examples: [
|
|
19672
|
-
`yarn gr ${
|
|
19814
|
+
`yarn gr ${path4.join(" ")} --org-uuid $ORG_UUID --domain-uuid $DOMAIN_UUID --allow-write`
|
|
19673
19815
|
],
|
|
19674
19816
|
handler: async (hctx) => {
|
|
19675
19817
|
const result2 = await requireAuth(hctx.ctx).apiClient.verifyOrgDomainSite(
|
|
@@ -19680,9 +19822,9 @@ function siteVerifyCommand(path, description) {
|
|
|
19680
19822
|
}
|
|
19681
19823
|
};
|
|
19682
19824
|
}
|
|
19683
|
-
function registerDomainCommand(
|
|
19825
|
+
function registerDomainCommand(path4) {
|
|
19684
19826
|
return {
|
|
19685
|
-
path,
|
|
19827
|
+
path: path4,
|
|
19686
19828
|
description: "Register an organization domain and return ownership TXT instructions",
|
|
19687
19829
|
mutates: true,
|
|
19688
19830
|
requiresWorkspace: false,
|
|
@@ -19691,7 +19833,7 @@ function registerDomainCommand(path) {
|
|
|
19691
19833
|
{ name: "domain", description: "Domain to register, e.g. example.com", required: true }
|
|
19692
19834
|
],
|
|
19693
19835
|
examples: [
|
|
19694
|
-
`yarn gr ${
|
|
19836
|
+
`yarn gr ${path4.join(" ")} --org-uuid $ORG_UUID --domain example.com --allow-write`
|
|
19695
19837
|
],
|
|
19696
19838
|
handler: async (hctx) => {
|
|
19697
19839
|
const result2 = await requireAuth(hctx.ctx).apiClient.registerOrgDomain(
|
|
@@ -20045,6 +20187,12 @@ var commands2 = [
|
|
|
20045
20187
|
workspaceUuid
|
|
20046
20188
|
);
|
|
20047
20189
|
outputSingle(result2, hctx.ctx, PROVIDER_PHONE_COLUMNS);
|
|
20190
|
+
process.stdout.write(
|
|
20191
|
+
`
|
|
20192
|
+
If workspace phone list is still empty, create a workspace phone record then:
|
|
20193
|
+
gr phone-numbers link -w ${workspaceUuid} --id <workspace-phone-id> --provider-phone-uuid ${providerPhoneUuid} --allow-write
|
|
20194
|
+
`
|
|
20195
|
+
);
|
|
20048
20196
|
}
|
|
20049
20197
|
},
|
|
20050
20198
|
{
|
|
@@ -20148,6 +20296,31 @@ var phoneNumbersDomain = {
|
|
|
20148
20296
|
outputData(result2, ctx, COLUMNS25);
|
|
20149
20297
|
}
|
|
20150
20298
|
},
|
|
20299
|
+
{
|
|
20300
|
+
path: ["phone-numbers", "link"],
|
|
20301
|
+
description: "Link a workspace phone number to a provider (org) phone",
|
|
20302
|
+
mutates: true,
|
|
20303
|
+
requiresWorkspace: true,
|
|
20304
|
+
args: [
|
|
20305
|
+
{
|
|
20306
|
+
name: "phone-number-id",
|
|
20307
|
+
aliases: ["id"],
|
|
20308
|
+
description: "Workspace phone number ID",
|
|
20309
|
+
required: true
|
|
20310
|
+
},
|
|
20311
|
+
{ name: "provider-phone-uuid", description: "Provider phone UUID", required: true }
|
|
20312
|
+
],
|
|
20313
|
+
examples: [
|
|
20314
|
+
"gr phone-numbers link -w $WID --id 1 --provider-phone-uuid <uuid> --allow-write"
|
|
20315
|
+
],
|
|
20316
|
+
handler: async ({ ctx, workspace, args }) => {
|
|
20317
|
+
const auth = requireAuth(ctx);
|
|
20318
|
+
const id = requireNumberFrom(args, "phone-number-id", "id");
|
|
20319
|
+
const providerPhoneUuid = requireString(args, "provider-phone-uuid");
|
|
20320
|
+
const result2 = await auth.apiClient.linkPhoneNumber(workspace, id, providerPhoneUuid);
|
|
20321
|
+
outputData([result2], auth, COLUMNS25);
|
|
20322
|
+
}
|
|
20323
|
+
},
|
|
20151
20324
|
{
|
|
20152
20325
|
path: ["phone-numbers", "request"],
|
|
20153
20326
|
description: "Request a phone number",
|
|
@@ -20166,7 +20339,12 @@ var phoneNumbersDomain = {
|
|
|
20166
20339
|
description: "List calls for a phone number",
|
|
20167
20340
|
requiresWorkspace: true,
|
|
20168
20341
|
args: [
|
|
20169
|
-
{
|
|
20342
|
+
{
|
|
20343
|
+
name: "phone-number-id",
|
|
20344
|
+
aliases: ["id"],
|
|
20345
|
+
description: "Phone number ID",
|
|
20346
|
+
required: true
|
|
20347
|
+
},
|
|
20170
20348
|
{ name: "page", description: "Page number" },
|
|
20171
20349
|
{ name: "page-size", description: "Page size" },
|
|
20172
20350
|
{ name: "status", description: "Filter by status" }
|
|
@@ -20175,7 +20353,7 @@ var phoneNumbersDomain = {
|
|
|
20175
20353
|
const status = args.status;
|
|
20176
20354
|
const result2 = await requireAuth(ctx).apiClient.getPhoneNumberCalls(
|
|
20177
20355
|
workspace,
|
|
20178
|
-
|
|
20356
|
+
requireNumberFrom(args, "phone-number-id", "id"),
|
|
20179
20357
|
args.page ? Number(args.page) : void 0,
|
|
20180
20358
|
args["page-size"] ? Number(args["page-size"]) : void 0,
|
|
20181
20359
|
status ? { status } : void 0
|
|
@@ -20188,14 +20366,19 @@ var phoneNumbersDomain = {
|
|
|
20188
20366
|
description: "List messages for a phone number",
|
|
20189
20367
|
requiresWorkspace: true,
|
|
20190
20368
|
args: [
|
|
20191
|
-
{
|
|
20369
|
+
{
|
|
20370
|
+
name: "phone-number-id",
|
|
20371
|
+
aliases: ["id"],
|
|
20372
|
+
description: "Phone number ID",
|
|
20373
|
+
required: true
|
|
20374
|
+
},
|
|
20192
20375
|
{ name: "page", description: "Page number" },
|
|
20193
20376
|
{ name: "page-size", description: "Page size" }
|
|
20194
20377
|
],
|
|
20195
20378
|
handler: async ({ ctx, workspace, args }) => {
|
|
20196
20379
|
const result2 = await requireAuth(ctx).apiClient.getPhoneNumberMessages(
|
|
20197
20380
|
workspace,
|
|
20198
|
-
|
|
20381
|
+
requireNumberFrom(args, "phone-number-id", "id"),
|
|
20199
20382
|
args.page ? Number(args.page) : void 0,
|
|
20200
20383
|
args["page-size"] ? Number(args["page-size"]) : void 0
|
|
20201
20384
|
);
|
|
@@ -20205,36 +20388,85 @@ var phoneNumbersDomain = {
|
|
|
20205
20388
|
]
|
|
20206
20389
|
};
|
|
20207
20390
|
|
|
20391
|
+
// src/lib/resolve-org.ts
|
|
20392
|
+
async function resolveOrgUuidFromWorkspace(apiClient, workspaceUuid) {
|
|
20393
|
+
const workspace = await apiClient.getWorkspace(workspaceUuid);
|
|
20394
|
+
const orgUuid = workspace.organizationUuid;
|
|
20395
|
+
if (!orgUuid) {
|
|
20396
|
+
throw new CLIError(
|
|
20397
|
+
"Workspace has no organization; pass --org-uuid for organization-scoped commands"
|
|
20398
|
+
);
|
|
20399
|
+
}
|
|
20400
|
+
return orgUuid;
|
|
20401
|
+
}
|
|
20402
|
+
function normalizePronunciationCreatePayload(data) {
|
|
20403
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) {
|
|
20404
|
+
throw new CLIError("JSON body required with term and respelling (--data or --file)");
|
|
20405
|
+
}
|
|
20406
|
+
const raw = data;
|
|
20407
|
+
const term = raw.term ?? raw.word;
|
|
20408
|
+
const respelling = raw.respelling ?? raw.pronunciation;
|
|
20409
|
+
if (typeof term !== "string" || !term) {
|
|
20410
|
+
throw new CLIError("Payload requires term (alias: word)");
|
|
20411
|
+
}
|
|
20412
|
+
if (typeof respelling !== "string" || !respelling) {
|
|
20413
|
+
throw new CLIError("Payload requires respelling (alias: pronunciation)");
|
|
20414
|
+
}
|
|
20415
|
+
const payload = { term, respelling };
|
|
20416
|
+
if (raw.accountId !== void 0 && raw.accountId !== null) {
|
|
20417
|
+
payload.accountId = Number(raw.accountId);
|
|
20418
|
+
}
|
|
20419
|
+
return payload;
|
|
20420
|
+
}
|
|
20421
|
+
function normalizePronunciationUpdatePayload(data) {
|
|
20422
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) {
|
|
20423
|
+
throw new CLIError("JSON body required (--data or --file)");
|
|
20424
|
+
}
|
|
20425
|
+
const raw = data;
|
|
20426
|
+
const respelling = raw.respelling ?? raw.pronunciation;
|
|
20427
|
+
if (typeof respelling !== "string" || !respelling) {
|
|
20428
|
+
throw new CLIError("Payload requires respelling (alias: pronunciation)");
|
|
20429
|
+
}
|
|
20430
|
+
return { respelling };
|
|
20431
|
+
}
|
|
20432
|
+
|
|
20208
20433
|
// src/domains/pronunciations.ts
|
|
20209
20434
|
var COLUMNS26 = [
|
|
20210
20435
|
COL.id(),
|
|
20211
|
-
{ header: "
|
|
20212
|
-
{ header: "
|
|
20213
|
-
{ header: "
|
|
20436
|
+
{ header: "TERM", getValue: (item) => item.term ?? "" },
|
|
20437
|
+
{ header: "RESPELLING", getValue: (item) => item.respelling ?? "" },
|
|
20438
|
+
{ header: "SCOPE", getValue: (item) => item.scope ?? "" }
|
|
20439
|
+
];
|
|
20440
|
+
var CREATE_EXAMPLES = [
|
|
20441
|
+
`gr pronunciations create -w $WID --data '{"term":"Jeeves","respelling":"JEEVZ"}' --allow-write`,
|
|
20442
|
+
" (aliases: word \u2192 term, pronunciation \u2192 respelling)"
|
|
20214
20443
|
];
|
|
20215
20444
|
var pronunciationsDomain = {
|
|
20216
20445
|
name: "pronunciations",
|
|
20217
|
-
description: "Manage pronunciation entries",
|
|
20446
|
+
description: "Manage pronunciation entries (org-scoped; -w resolves organization)",
|
|
20218
20447
|
commands: [
|
|
20219
20448
|
{
|
|
20220
20449
|
path: ["pronunciations", "list"],
|
|
20221
|
-
description: "List pronunciation entries",
|
|
20450
|
+
description: "List pronunciation entries for the workspace organization",
|
|
20222
20451
|
requiresWorkspace: true,
|
|
20223
20452
|
handler: async ({ ctx, workspace }) => {
|
|
20224
|
-
const
|
|
20453
|
+
const auth = requireAuth(ctx);
|
|
20454
|
+
const orgUuid = await resolveOrgUuidFromWorkspace(auth.apiClient, workspace);
|
|
20455
|
+
const result2 = await auth.apiClient.getPronunciationEntries(orgUuid);
|
|
20225
20456
|
outputData(result2, ctx, COLUMNS26);
|
|
20226
20457
|
}
|
|
20227
20458
|
},
|
|
20228
20459
|
{
|
|
20229
20460
|
path: ["pronunciations", "create"],
|
|
20230
|
-
description: "Create a pronunciation entry",
|
|
20461
|
+
description: "Create a pronunciation entry (term + respelling)",
|
|
20231
20462
|
mutates: true,
|
|
20232
20463
|
requiresWorkspace: true,
|
|
20464
|
+
examples: CREATE_EXAMPLES,
|
|
20233
20465
|
handler: async ({ ctx, workspace, data }) => {
|
|
20234
|
-
const
|
|
20235
|
-
|
|
20236
|
-
|
|
20237
|
-
);
|
|
20466
|
+
const auth = requireAuth(ctx);
|
|
20467
|
+
const orgUuid = await resolveOrgUuidFromWorkspace(auth.apiClient, workspace);
|
|
20468
|
+
const payload = normalizePronunciationCreatePayload(data);
|
|
20469
|
+
const result2 = await auth.apiClient.createPronunciationEntry(orgUuid, payload);
|
|
20238
20470
|
outputSingle(result2, ctx, COLUMNS26);
|
|
20239
20471
|
}
|
|
20240
20472
|
},
|
|
@@ -20245,10 +20477,13 @@ var pronunciationsDomain = {
|
|
|
20245
20477
|
requiresWorkspace: true,
|
|
20246
20478
|
args: [{ name: "id", description: "Pronunciation entry ID", required: true }],
|
|
20247
20479
|
handler: async ({ ctx, workspace, args, data }) => {
|
|
20248
|
-
const
|
|
20249
|
-
|
|
20480
|
+
const auth = requireAuth(ctx);
|
|
20481
|
+
const orgUuid = await resolveOrgUuidFromWorkspace(auth.apiClient, workspace);
|
|
20482
|
+
const payload = normalizePronunciationUpdatePayload(data);
|
|
20483
|
+
const result2 = await auth.apiClient.updatePronunciationEntry(
|
|
20484
|
+
orgUuid,
|
|
20250
20485
|
requireNumber(args, "id"),
|
|
20251
|
-
|
|
20486
|
+
payload
|
|
20252
20487
|
);
|
|
20253
20488
|
outputSingle(result2, ctx, COLUMNS26);
|
|
20254
20489
|
}
|
|
@@ -20260,8 +20495,10 @@ var pronunciationsDomain = {
|
|
|
20260
20495
|
requiresWorkspace: true,
|
|
20261
20496
|
args: [{ name: "id", description: "Pronunciation entry ID", required: true }],
|
|
20262
20497
|
handler: async ({ ctx, workspace, args }) => {
|
|
20498
|
+
const auth = requireAuth(ctx);
|
|
20499
|
+
const orgUuid = await resolveOrgUuidFromWorkspace(auth.apiClient, workspace);
|
|
20263
20500
|
const id = requireNumber(args, "id");
|
|
20264
|
-
await
|
|
20501
|
+
await auth.apiClient.deletePronunciationEntry(orgUuid, id);
|
|
20265
20502
|
process.stdout.write(`Deleted pronunciation entry ${id}
|
|
20266
20503
|
`);
|
|
20267
20504
|
}
|
|
@@ -20281,7 +20518,8 @@ var pronunciationsDomain = {
|
|
|
20281
20518
|
mutates: true,
|
|
20282
20519
|
requiresWorkspace: true,
|
|
20283
20520
|
handler: async ({ ctx, data }) => {
|
|
20284
|
-
const
|
|
20521
|
+
const payload = normalizePronunciationCreatePayload(data);
|
|
20522
|
+
const result2 = await requireAuth(ctx).apiClient.createMyPronunciation(payload);
|
|
20285
20523
|
outputSingle(result2, ctx, COLUMNS26);
|
|
20286
20524
|
}
|
|
20287
20525
|
},
|
|
@@ -20289,11 +20527,17 @@ var pronunciationsDomain = {
|
|
|
20289
20527
|
path: ["pronunciations", "suggest"],
|
|
20290
20528
|
description: "Get pronunciation suggestions for a word",
|
|
20291
20529
|
requiresWorkspace: true,
|
|
20292
|
-
args: [
|
|
20530
|
+
args: [
|
|
20531
|
+
{
|
|
20532
|
+
name: "word",
|
|
20533
|
+
aliases: ["term"],
|
|
20534
|
+
description: "Word to get suggestions for",
|
|
20535
|
+
required: true
|
|
20536
|
+
}
|
|
20537
|
+
],
|
|
20293
20538
|
handler: async ({ ctx, args }) => {
|
|
20294
|
-
const
|
|
20295
|
-
|
|
20296
|
-
);
|
|
20539
|
+
const word = typeof args.word === "string" ? args.word : typeof args.term === "string" ? args.term : requireString(args, "word");
|
|
20540
|
+
const result2 = await requireAuth(ctx).apiClient.suggestPronunciations(word);
|
|
20297
20541
|
outputData(result2, ctx);
|
|
20298
20542
|
}
|
|
20299
20543
|
},
|
|
@@ -22250,6 +22494,327 @@ function openUrl(url) {
|
|
|
22250
22494
|
}
|
|
22251
22495
|
});
|
|
22252
22496
|
}
|
|
22497
|
+
function buildCliConciergeAppContext() {
|
|
22498
|
+
const shell = process.env.SHELL ? path3.basename(process.env.SHELL) : void 0;
|
|
22499
|
+
return {
|
|
22500
|
+
clientSurface: "cli",
|
|
22501
|
+
channel: "text",
|
|
22502
|
+
cliContext: {
|
|
22503
|
+
shell,
|
|
22504
|
+
os: process.platform,
|
|
22505
|
+
cwdBasename: path3.basename(process.cwd())
|
|
22506
|
+
}
|
|
22507
|
+
};
|
|
22508
|
+
}
|
|
22509
|
+
var DEFAULT_MAX_READ_BYTES = 5e4;
|
|
22510
|
+
var ABSOLUTE_MAX_READ_BYTES = 1e6;
|
|
22511
|
+
function resolveSafePath(root, relativePath) {
|
|
22512
|
+
const rootResolved = path3.resolve(root);
|
|
22513
|
+
const target = path3.resolve(rootResolved, relativePath || ".");
|
|
22514
|
+
if (target !== rootResolved && !target.startsWith(rootResolved + path3.sep)) {
|
|
22515
|
+
return null;
|
|
22516
|
+
}
|
|
22517
|
+
let cursor = rootResolved;
|
|
22518
|
+
const rest = path3.relative(rootResolved, target);
|
|
22519
|
+
if (rest && rest !== ".") {
|
|
22520
|
+
for (const segment of rest.split(path3.sep)) {
|
|
22521
|
+
cursor = path3.join(cursor, segment);
|
|
22522
|
+
try {
|
|
22523
|
+
const lstat = fs.lstatSync(cursor);
|
|
22524
|
+
if (lstat.isSymbolicLink()) {
|
|
22525
|
+
return null;
|
|
22526
|
+
}
|
|
22527
|
+
} catch {
|
|
22528
|
+
break;
|
|
22529
|
+
}
|
|
22530
|
+
}
|
|
22531
|
+
}
|
|
22532
|
+
return target;
|
|
22533
|
+
}
|
|
22534
|
+
function executeCliLocalFileAction(root, interrupt, opts) {
|
|
22535
|
+
const safe = resolveSafePath(root, interrupt.path);
|
|
22536
|
+
if (!safe) {
|
|
22537
|
+
return { ok: false, error: "Path escapes trusted directory" };
|
|
22538
|
+
}
|
|
22539
|
+
try {
|
|
22540
|
+
switch (interrupt.action) {
|
|
22541
|
+
case "read_file": {
|
|
22542
|
+
if (!fs.existsSync(safe)) {
|
|
22543
|
+
return { ok: false, error: "File not found" };
|
|
22544
|
+
}
|
|
22545
|
+
const stat = fs.lstatSync(safe);
|
|
22546
|
+
if (stat.isSymbolicLink()) {
|
|
22547
|
+
return { ok: false, error: "Path escapes trusted directory" };
|
|
22548
|
+
}
|
|
22549
|
+
if (!stat.isFile()) {
|
|
22550
|
+
return { ok: false, error: "Not a file" };
|
|
22551
|
+
}
|
|
22552
|
+
const requested = Number.isFinite(interrupt.max_bytes) ? Math.max(1, Math.min(Number(interrupt.max_bytes), ABSOLUTE_MAX_READ_BYTES)) : DEFAULT_MAX_READ_BYTES;
|
|
22553
|
+
if (stat.size > requested) {
|
|
22554
|
+
const buf = fs.readFileSync(safe, { encoding: "utf8" });
|
|
22555
|
+
return {
|
|
22556
|
+
ok: true,
|
|
22557
|
+
content: buf.slice(0, requested) + `
|
|
22558
|
+
|
|
22559
|
+
[truncated at ${requested} bytes]`
|
|
22560
|
+
};
|
|
22561
|
+
}
|
|
22562
|
+
return { ok: true, content: fs.readFileSync(safe, "utf8") };
|
|
22563
|
+
}
|
|
22564
|
+
case "list_dir": {
|
|
22565
|
+
if (!fs.existsSync(safe)) {
|
|
22566
|
+
return { ok: false, error: "Directory not found" };
|
|
22567
|
+
}
|
|
22568
|
+
const entries = fs.readdirSync(safe, { withFileTypes: true });
|
|
22569
|
+
const lines = entries.slice(0, 200).map((e) => e.isDirectory() ? `${e.name}/` : e.name);
|
|
22570
|
+
if (entries.length > 200) {
|
|
22571
|
+
lines.push(`... and ${entries.length - 200} more`);
|
|
22572
|
+
}
|
|
22573
|
+
return { ok: true, content: lines.join("\n") };
|
|
22574
|
+
}
|
|
22575
|
+
case "write_file": {
|
|
22576
|
+
if (!opts.allowWrite) {
|
|
22577
|
+
return { ok: false, error: "Write requires --allow-write" };
|
|
22578
|
+
}
|
|
22579
|
+
if (interrupt.content === void 0) {
|
|
22580
|
+
return { ok: false, error: "Missing content for write_file" };
|
|
22581
|
+
}
|
|
22582
|
+
fs.mkdirSync(path3.dirname(safe), { recursive: true });
|
|
22583
|
+
fs.writeFileSync(safe, interrupt.content, "utf8");
|
|
22584
|
+
return { ok: true, content: "written" };
|
|
22585
|
+
}
|
|
22586
|
+
default:
|
|
22587
|
+
return { ok: false, error: `Unknown action: ${interrupt.action}` };
|
|
22588
|
+
}
|
|
22589
|
+
} catch (e) {
|
|
22590
|
+
return { ok: false, error: sanitizeLocalFileError(e) };
|
|
22591
|
+
}
|
|
22592
|
+
}
|
|
22593
|
+
function sanitizeLocalFileError(e) {
|
|
22594
|
+
if (e && typeof e === "object" && "code" in e) {
|
|
22595
|
+
const code = e.code;
|
|
22596
|
+
if (code === "ENOENT") return "File or directory not found";
|
|
22597
|
+
if (code === "EACCES" || code === "EPERM") return "Permission denied";
|
|
22598
|
+
if (code === "EISDIR") return "Not a file";
|
|
22599
|
+
if (code === "ENOTDIR") return "Not a directory";
|
|
22600
|
+
}
|
|
22601
|
+
return "Local file operation failed";
|
|
22602
|
+
}
|
|
22603
|
+
function isCliLocalFileInterrupt(value) {
|
|
22604
|
+
if (typeof value !== "object" || value === null) return false;
|
|
22605
|
+
const v = value;
|
|
22606
|
+
return v.type === "cli_local_file" && typeof v.action === "string" && typeof v.path === "string";
|
|
22607
|
+
}
|
|
22608
|
+
var STORE_VERSION = 1;
|
|
22609
|
+
function storePath() {
|
|
22610
|
+
const base = process.env.XDG_CONFIG_HOME ?? (process.platform === "win32" ? path3.join(process.env.APPDATA ?? os.homedir(), "gravityrail") : path3.join(os.homedir(), ".config", "gravityrail"));
|
|
22611
|
+
return path3.join(base, "trusted-dirs.json");
|
|
22612
|
+
}
|
|
22613
|
+
function loadTrustedDirsStore() {
|
|
22614
|
+
const file = storePath();
|
|
22615
|
+
try {
|
|
22616
|
+
const raw = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
22617
|
+
if (raw?.version === STORE_VERSION && Array.isArray(raw.always)) {
|
|
22618
|
+
return { version: STORE_VERSION, always: raw.always.map((p) => path3.resolve(p)) };
|
|
22619
|
+
}
|
|
22620
|
+
} catch {
|
|
22621
|
+
}
|
|
22622
|
+
return { version: STORE_VERSION, always: [] };
|
|
22623
|
+
}
|
|
22624
|
+
function saveTrustedDirsStore(store) {
|
|
22625
|
+
const file = storePath();
|
|
22626
|
+
const dir = path3.dirname(file);
|
|
22627
|
+
fs.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
22628
|
+
fs.writeFileSync(file, JSON.stringify(store, null, 2) + "\n", { mode: 384 });
|
|
22629
|
+
}
|
|
22630
|
+
function isDirectoryTrusted(dirPath, opts) {
|
|
22631
|
+
if (opts?.trustCwdFlag) return true;
|
|
22632
|
+
const resolved = path3.resolve(dirPath);
|
|
22633
|
+
const store = loadTrustedDirsStore();
|
|
22634
|
+
return store.always.includes(resolved);
|
|
22635
|
+
}
|
|
22636
|
+
async function promptTrustInteractive(dirPath) {
|
|
22637
|
+
const rl = readline__default.createInterface({ input: process.stdin, output: process.stderr });
|
|
22638
|
+
const question = (q) => new Promise((resolve2) => rl.question(q, resolve2));
|
|
22639
|
+
process.stderr.write(
|
|
22640
|
+
`
|
|
22641
|
+
Gravity Rail may read files in:
|
|
22642
|
+
${dirPath}
|
|
22643
|
+
|
|
22644
|
+
File contents can influence the assistant. Do you trust the files in this directory?
|
|
22645
|
+
[1] Trust once (this session)
|
|
22646
|
+
[2] Trust always (remember for this path)
|
|
22647
|
+
[3] No \u2014 do not read local files
|
|
22648
|
+
|
|
22649
|
+
Choice [1/2/3] (default 3): `
|
|
22650
|
+
);
|
|
22651
|
+
const answer = (await question("")).trim() || "3";
|
|
22652
|
+
rl.close();
|
|
22653
|
+
if (answer === "1") return "once";
|
|
22654
|
+
if (answer === "2") return "always";
|
|
22655
|
+
return "denied";
|
|
22656
|
+
}
|
|
22657
|
+
var sessionTrusted = /* @__PURE__ */ new Set();
|
|
22658
|
+
async function resolveDirectoryTrust(opts) {
|
|
22659
|
+
const dir = path3.resolve(opts.trustDir ?? opts.cwd ?? process.cwd());
|
|
22660
|
+
if (isDirectoryTrusted(dir, { trustCwdFlag: opts.trustCwd })) return "trusted";
|
|
22661
|
+
if (sessionTrusted.has(dir)) return "trusted";
|
|
22662
|
+
if (opts.trustDir && path3.resolve(opts.trustDir) === dir) {
|
|
22663
|
+
sessionTrusted.add(dir);
|
|
22664
|
+
return "trusted";
|
|
22665
|
+
}
|
|
22666
|
+
if (opts.trustCwd) {
|
|
22667
|
+
sessionTrusted.add(dir);
|
|
22668
|
+
return "trusted";
|
|
22669
|
+
}
|
|
22670
|
+
if (!process.stdin.isTTY || opts.nonInteractive) {
|
|
22671
|
+
return "denied";
|
|
22672
|
+
}
|
|
22673
|
+
const decision = await promptTrustInteractive(dir);
|
|
22674
|
+
if (decision === "denied") {
|
|
22675
|
+
return "denied";
|
|
22676
|
+
}
|
|
22677
|
+
if (decision === "always") {
|
|
22678
|
+
const store = loadTrustedDirsStore();
|
|
22679
|
+
if (!store.always.includes(dir)) {
|
|
22680
|
+
store.always.push(dir);
|
|
22681
|
+
saveTrustedDirsStore(store);
|
|
22682
|
+
}
|
|
22683
|
+
}
|
|
22684
|
+
sessionTrusted.add(dir);
|
|
22685
|
+
return "trusted";
|
|
22686
|
+
}
|
|
22687
|
+
|
|
22688
|
+
// src/lib/cli-hitl.ts
|
|
22689
|
+
function extractHumanInputRequest(event) {
|
|
22690
|
+
const type = String(event.type ?? "").toUpperCase();
|
|
22691
|
+
if (type === "CUSTOM" && event.name === "human_input_request") {
|
|
22692
|
+
return event.value;
|
|
22693
|
+
}
|
|
22694
|
+
if (event.name === "human_input_request") {
|
|
22695
|
+
return event.value;
|
|
22696
|
+
}
|
|
22697
|
+
return null;
|
|
22698
|
+
}
|
|
22699
|
+
async function handleCliHitlInterrupt(value, opts) {
|
|
22700
|
+
if (!value || typeof value !== "object") return null;
|
|
22701
|
+
const payload = value;
|
|
22702
|
+
if (isCliLocalFileInterrupt(payload)) {
|
|
22703
|
+
const interrupt = payload;
|
|
22704
|
+
if (typeof payload.interrupt_id === "string") {
|
|
22705
|
+
interrupt.interrupt_id = payload.interrupt_id;
|
|
22706
|
+
}
|
|
22707
|
+
return handleCliLocalFile(interrupt, opts);
|
|
22708
|
+
}
|
|
22709
|
+
if (payload.type === "present_choice") {
|
|
22710
|
+
return handlePresentChoiceTerminal(payload);
|
|
22711
|
+
}
|
|
22712
|
+
return null;
|
|
22713
|
+
}
|
|
22714
|
+
async function handleCliLocalFile(interrupt, opts) {
|
|
22715
|
+
const trust = await resolveDirectoryTrust({
|
|
22716
|
+
cwd: process.cwd(),
|
|
22717
|
+
trustCwd: opts.trustCwd,
|
|
22718
|
+
trustDir: opts.trustDir,
|
|
22719
|
+
// Interactive sessions: prompt on first local-file use (review feedback).
|
|
22720
|
+
nonInteractive: opts.nonInteractive ?? process.stdin.isTTY !== true
|
|
22721
|
+
});
|
|
22722
|
+
if (trust !== "trusted") {
|
|
22723
|
+
return {
|
|
22724
|
+
resumeValue: JSON.stringify({
|
|
22725
|
+
ok: false,
|
|
22726
|
+
error: "Directory not trusted for local file access. Re-run with --trust-cwd or --trust-dir <path>, or approve trust interactively when prompted."
|
|
22727
|
+
}),
|
|
22728
|
+
resumeInterruptId: interrupt.interrupt_id
|
|
22729
|
+
};
|
|
22730
|
+
}
|
|
22731
|
+
process.stderr.write(`
|
|
22732
|
+
[local ${interrupt.action}] ${interrupt.path}
|
|
22733
|
+
`);
|
|
22734
|
+
const result2 = executeCliLocalFileAction(process.cwd(), interrupt, {
|
|
22735
|
+
allowWrite: opts.allowWrite
|
|
22736
|
+
});
|
|
22737
|
+
return {
|
|
22738
|
+
resumeValue: JSON.stringify(result2),
|
|
22739
|
+
resumeInterruptId: interrupt.interrupt_id
|
|
22740
|
+
};
|
|
22741
|
+
}
|
|
22742
|
+
async function handlePresentChoiceTerminal(payload) {
|
|
22743
|
+
const prompt = typeof payload.user_prompt === "string" && payload.user_prompt || typeof payload.description === "string" && payload.description || "Choose an option";
|
|
22744
|
+
const markup = typeof payload.openui_markup === "string" ? payload.openui_markup : "";
|
|
22745
|
+
process.stderr.write(`
|
|
22746
|
+
${prompt}
|
|
22747
|
+
`);
|
|
22748
|
+
if (markup) {
|
|
22749
|
+
process.stderr.write(`${markup.slice(0, 2e3)}
|
|
22750
|
+
`);
|
|
22751
|
+
}
|
|
22752
|
+
if (!process.stdin.isTTY) {
|
|
22753
|
+
return {
|
|
22754
|
+
resumeValue: "",
|
|
22755
|
+
resumeInterruptId: typeof payload.interrupt_id === "string" ? payload.interrupt_id : void 0
|
|
22756
|
+
};
|
|
22757
|
+
}
|
|
22758
|
+
const rl = readline__default.createInterface({ input: process.stdin, output: process.stderr });
|
|
22759
|
+
const answer = await new Promise((resolve2) => {
|
|
22760
|
+
rl.question("Your choice (value): ", resolve2);
|
|
22761
|
+
});
|
|
22762
|
+
rl.close();
|
|
22763
|
+
return {
|
|
22764
|
+
resumeValue: answer.trim(),
|
|
22765
|
+
resumeInterruptId: typeof payload.interrupt_id === "string" ? payload.interrupt_id : void 0
|
|
22766
|
+
};
|
|
22767
|
+
}
|
|
22768
|
+
|
|
22769
|
+
// src/lib/concierge-agui-loop.ts
|
|
22770
|
+
var MAX_HITL_ROUNDS = 25;
|
|
22771
|
+
async function runConciergeAguiLoop(client, initialMessages, onEvent, opts) {
|
|
22772
|
+
let streamText = "";
|
|
22773
|
+
let hasToolCalls = false;
|
|
22774
|
+
let resume;
|
|
22775
|
+
for (let round = 0; round < MAX_HITL_ROUNDS; round++) {
|
|
22776
|
+
let hitlPayload = null;
|
|
22777
|
+
await client.streamRequest(
|
|
22778
|
+
resume ? {
|
|
22779
|
+
messages: [],
|
|
22780
|
+
resumeValue: resume.resumeValue,
|
|
22781
|
+
resumeInterruptId: resume.resumeInterruptId
|
|
22782
|
+
} : { messages: initialMessages },
|
|
22783
|
+
(event) => {
|
|
22784
|
+
onEvent(event);
|
|
22785
|
+
const delta = getTextDelta(event);
|
|
22786
|
+
if (delta) streamText += delta;
|
|
22787
|
+
const type = event.type?.toLowerCase() ?? "";
|
|
22788
|
+
if (type === "tool_call_start") hasToolCalls = true;
|
|
22789
|
+
const errorMsg = getErrorMessage(event);
|
|
22790
|
+
if (errorMsg) process.stderr.write(`\x1B[31mError: ${errorMsg}\x1B[0m
|
|
22791
|
+
`);
|
|
22792
|
+
const hitl = extractHumanInputRequest(event);
|
|
22793
|
+
if (hitl) hitlPayload = hitl;
|
|
22794
|
+
},
|
|
22795
|
+
{ autoApprove: opts.autoApprove }
|
|
22796
|
+
);
|
|
22797
|
+
if (!hitlPayload) {
|
|
22798
|
+
break;
|
|
22799
|
+
}
|
|
22800
|
+
const next = await handleCliHitlInterrupt(hitlPayload, {
|
|
22801
|
+
allowWrite: opts.allowWrite,
|
|
22802
|
+
autoApprove: opts.autoApprove,
|
|
22803
|
+
trustCwd: opts.trustCwd,
|
|
22804
|
+
trustDir: opts.trustDir,
|
|
22805
|
+
nonInteractive: opts.nonInteractive
|
|
22806
|
+
});
|
|
22807
|
+
if (!next) {
|
|
22808
|
+
process.stderr.write(
|
|
22809
|
+
" [HITL] Unsupported interrupt in CLI \u2014 open app.gravityrail.com/setup to continue.\n"
|
|
22810
|
+
);
|
|
22811
|
+
break;
|
|
22812
|
+
}
|
|
22813
|
+
resume = next;
|
|
22814
|
+
streamText = "";
|
|
22815
|
+
}
|
|
22816
|
+
return { streamText, hasToolCalls };
|
|
22817
|
+
}
|
|
22253
22818
|
var RealtimeClient = class extends EventEmitter {
|
|
22254
22819
|
ws = null;
|
|
22255
22820
|
connected = false;
|
|
@@ -22269,7 +22834,7 @@ var RealtimeClient = class extends EventEmitter {
|
|
|
22269
22834
|
new Error("WebSocket not available. Node.js 22+ is required for realtime mode.")
|
|
22270
22835
|
);
|
|
22271
22836
|
}
|
|
22272
|
-
return new Promise((
|
|
22837
|
+
return new Promise((resolve2, reject) => {
|
|
22273
22838
|
const params = new URLSearchParams();
|
|
22274
22839
|
if (this.options.vadMode) {
|
|
22275
22840
|
params.append("vad", this.options.vadMode);
|
|
@@ -22324,7 +22889,7 @@ var RealtimeClient = class extends EventEmitter {
|
|
|
22324
22889
|
}, 1e4);
|
|
22325
22890
|
this.once("connected", () => {
|
|
22326
22891
|
clearTimeout(timeout);
|
|
22327
|
-
|
|
22892
|
+
resolve2();
|
|
22328
22893
|
});
|
|
22329
22894
|
this.once("error", () => {
|
|
22330
22895
|
clearTimeout(timeout);
|
|
@@ -23289,7 +23854,7 @@ async function listOutputDevices() {
|
|
|
23289
23854
|
return listDevices("play");
|
|
23290
23855
|
}
|
|
23291
23856
|
function listDevices(command) {
|
|
23292
|
-
return new Promise((
|
|
23857
|
+
return new Promise((resolve2) => {
|
|
23293
23858
|
try {
|
|
23294
23859
|
const proc = spawnSync(command, ["--list-devices"], {
|
|
23295
23860
|
encoding: "utf-8",
|
|
@@ -23313,9 +23878,9 @@ function listDevices(command) {
|
|
|
23313
23878
|
if (devices.length === 0) {
|
|
23314
23879
|
devices.push({ id: "default", name: "System Default", isDefault: true });
|
|
23315
23880
|
}
|
|
23316
|
-
|
|
23881
|
+
resolve2(devices);
|
|
23317
23882
|
} catch {
|
|
23318
|
-
|
|
23883
|
+
resolve2([{ id: "default", name: "System Default", isDefault: true }]);
|
|
23319
23884
|
}
|
|
23320
23885
|
});
|
|
23321
23886
|
}
|
|
@@ -23337,14 +23902,14 @@ async function selectDevice(devices, label) {
|
|
|
23337
23902
|
process.stderr.write(` ${i + 1}. ${dev.name}${marker}
|
|
23338
23903
|
`);
|
|
23339
23904
|
}
|
|
23340
|
-
return new Promise((
|
|
23905
|
+
return new Promise((resolve2) => {
|
|
23341
23906
|
const defaultIdx = devices.findIndex((d) => d.isDefault);
|
|
23342
23907
|
const defaultNum = defaultIdx >= 0 ? defaultIdx + 1 : 1;
|
|
23343
23908
|
rl.question(` Select [${defaultNum}]: `, (answer) => {
|
|
23344
23909
|
rl.close();
|
|
23345
23910
|
const num = answer.trim() ? parseInt(answer.trim(), 10) : defaultNum;
|
|
23346
23911
|
const idx = Math.max(0, Math.min(devices.length - 1, num - 1));
|
|
23347
|
-
|
|
23912
|
+
resolve2(devices[idx]);
|
|
23348
23913
|
});
|
|
23349
23914
|
});
|
|
23350
23915
|
}
|
|
@@ -23602,56 +24167,51 @@ async function runChatSession(session, args) {
|
|
|
23602
24167
|
"Interactive mode requires a TTY. Use --message for non-interactive mode."
|
|
23603
24168
|
);
|
|
23604
24169
|
}
|
|
24170
|
+
const allowWrite = !!args["allow-write"] || !!session.allowWrite;
|
|
23605
24171
|
if (message) {
|
|
23606
|
-
await singleTurnChatSession(session, message, debug, autoApprove);
|
|
24172
|
+
await singleTurnChatSession(session, message, debug, autoApprove, allowWrite);
|
|
23607
24173
|
} else if (isVoice) {
|
|
23608
24174
|
await voiceChatSession(session, vadMode(args), args, debug);
|
|
23609
24175
|
} else if (isRealtime) {
|
|
23610
24176
|
await realtimeChatSession(session, vadMode(args), debug);
|
|
23611
24177
|
} else {
|
|
23612
|
-
await interactiveChatSession(session, debug);
|
|
24178
|
+
await interactiveChatSession(session, debug, autoApprove, allowWrite);
|
|
23613
24179
|
}
|
|
23614
24180
|
}
|
|
23615
24181
|
function vadMode(args) {
|
|
23616
24182
|
return typeof args.vad === "string" ? args.vad : "server";
|
|
23617
24183
|
}
|
|
23618
|
-
async function singleTurnChatSession(session, message, debug, autoApprove = false) {
|
|
24184
|
+
async function singleTurnChatSession(session, message, debug, autoApprove = false, allowWrite = false) {
|
|
23619
24185
|
const aguiClient = new AGUIClient({
|
|
23620
24186
|
baseUrl: session.ctx.envConfig.apiUrl,
|
|
24187
|
+
workspaceUuid: session.workspaceUuid,
|
|
24188
|
+
chatId: session.chatId,
|
|
23621
24189
|
endpointUrl: session.aguiEndpointUrl,
|
|
23622
24190
|
accessToken: session.ctx.accessToken,
|
|
23623
24191
|
debug,
|
|
23624
|
-
model: session.model
|
|
24192
|
+
model: session.model,
|
|
24193
|
+
appContext: session.appContext
|
|
23625
24194
|
});
|
|
23626
|
-
|
|
23627
|
-
|
|
23628
|
-
|
|
23629
|
-
|
|
23630
|
-
|
|
23631
|
-
const logStream = logFile && fs ? fs.createWriteStream(logFile, { flags: "a" }) : null;
|
|
23632
|
-
await aguiClient.sendMessage(
|
|
23633
|
-
[{ role: "user", content: message }],
|
|
23634
|
-
(event) => {
|
|
23635
|
-
eventCount++;
|
|
23636
|
-
if (logStream) logStream.write(JSON.stringify(event) + "\n");
|
|
23637
|
-
const delta = getTextDelta(event);
|
|
23638
|
-
if (delta) streamText += delta;
|
|
23639
|
-
const type = event.type?.toLowerCase() ?? "";
|
|
23640
|
-
if (type === "tool_call_start") {
|
|
23641
|
-
hasToolCalls = true;
|
|
23642
|
-
const name = event.name ?? "";
|
|
23643
|
-
process.stderr.write(` [${name}]
|
|
23644
|
-
`);
|
|
23645
|
-
}
|
|
23646
|
-
const errorMsg = getErrorMessage(event);
|
|
23647
|
-
if (errorMsg) process.stderr.write(`\x1B[31mError: ${errorMsg}\x1B[0m
|
|
24195
|
+
const onEvent = (event) => {
|
|
24196
|
+
const type = event.type?.toLowerCase() ?? "";
|
|
24197
|
+
if (type === "tool_call_start") {
|
|
24198
|
+
const name = event.toolCallName ?? event.name ?? "";
|
|
24199
|
+
process.stderr.write(` [${name}]
|
|
23648
24200
|
`);
|
|
23649
|
-
}
|
|
23650
|
-
|
|
24201
|
+
}
|
|
24202
|
+
};
|
|
24203
|
+
const { streamText, hasToolCalls } = await runConciergeAguiLoop(
|
|
24204
|
+
aguiClient,
|
|
24205
|
+
[{ role: "user", content: message }],
|
|
24206
|
+
onEvent,
|
|
24207
|
+
{
|
|
24208
|
+
autoApprove,
|
|
24209
|
+
allowWrite: allowWrite || !!session.allowWrite,
|
|
24210
|
+
trustCwd: session.trustCwd,
|
|
24211
|
+
trustDir: session.trustDir,
|
|
24212
|
+
nonInteractive: true
|
|
24213
|
+
}
|
|
23651
24214
|
);
|
|
23652
|
-
if (logStream) logStream.end();
|
|
23653
|
-
if (debug) process.stderr.write(` (${eventCount} SSE events)
|
|
23654
|
-
`);
|
|
23655
24215
|
if (streamText) {
|
|
23656
24216
|
process.stdout.write(`\x1B[32mAssistant:\x1B[0m ${streamText}
|
|
23657
24217
|
`);
|
|
@@ -23685,13 +24245,16 @@ async function singleTurnChatSession(session, message, debug, autoApprove = fals
|
|
|
23685
24245
|
`);
|
|
23686
24246
|
}
|
|
23687
24247
|
}
|
|
23688
|
-
async function interactiveChatSession(session, debug) {
|
|
24248
|
+
async function interactiveChatSession(session, debug, autoApprove, allowWrite) {
|
|
23689
24249
|
const aguiClient = new AGUIClient({
|
|
23690
24250
|
baseUrl: session.ctx.envConfig.apiUrl,
|
|
24251
|
+
workspaceUuid: session.workspaceUuid,
|
|
24252
|
+
chatId: session.chatId,
|
|
23691
24253
|
endpointUrl: session.aguiEndpointUrl,
|
|
23692
24254
|
accessToken: session.ctx.accessToken,
|
|
23693
24255
|
debug,
|
|
23694
|
-
model: session.model
|
|
24256
|
+
model: session.model,
|
|
24257
|
+
appContext: session.appContext
|
|
23695
24258
|
});
|
|
23696
24259
|
clearScreen();
|
|
23697
24260
|
console.log("=".repeat(60));
|
|
@@ -23720,7 +24283,7 @@ async function interactiveChatSession(session, debug) {
|
|
|
23720
24283
|
}
|
|
23721
24284
|
}
|
|
23722
24285
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
23723
|
-
return new Promise((
|
|
24286
|
+
return new Promise((resolve2) => {
|
|
23724
24287
|
const prompt = () => {
|
|
23725
24288
|
rl.question("\x1B[34mYou:\x1B[0m ", async (input) => {
|
|
23726
24289
|
const trimmed = input.trim();
|
|
@@ -23737,18 +24300,34 @@ async function interactiveChatSession(session, debug) {
|
|
|
23737
24300
|
process.stdout.write("\n\x1B[32mAssistant:\x1B[0m ");
|
|
23738
24301
|
let assistantContent = "";
|
|
23739
24302
|
try {
|
|
23740
|
-
|
|
23741
|
-
|
|
23742
|
-
|
|
23743
|
-
|
|
23744
|
-
|
|
23745
|
-
|
|
23746
|
-
|
|
23747
|
-
|
|
24303
|
+
const { streamText } = await runConciergeAguiLoop(
|
|
24304
|
+
aguiClient,
|
|
24305
|
+
conversationMessages,
|
|
24306
|
+
(event) => {
|
|
24307
|
+
const delta = getTextDelta(event);
|
|
24308
|
+
if (delta) {
|
|
24309
|
+
process.stdout.write(delta);
|
|
24310
|
+
assistantContent += delta;
|
|
24311
|
+
}
|
|
24312
|
+
const errorMsg = getErrorMessage(event);
|
|
24313
|
+
if (errorMsg) console.log(`
|
|
23748
24314
|
\x1B[31mError: ${errorMsg}\x1B[0m`);
|
|
23749
|
-
|
|
23750
|
-
|
|
24315
|
+
},
|
|
24316
|
+
{
|
|
24317
|
+
autoApprove,
|
|
24318
|
+
allowWrite: allowWrite || !!session.allowWrite,
|
|
24319
|
+
debug,
|
|
24320
|
+
trustCwd: session.trustCwd,
|
|
24321
|
+
trustDir: session.trustDir
|
|
24322
|
+
}
|
|
24323
|
+
);
|
|
24324
|
+
if (streamText && !assistantContent) {
|
|
24325
|
+
assistantContent = streamText;
|
|
24326
|
+
process.stdout.write(streamText);
|
|
24327
|
+
}
|
|
24328
|
+
if (assistantContent) {
|
|
23751
24329
|
conversationMessages.push({ role: "assistant", content: assistantContent });
|
|
24330
|
+
}
|
|
23752
24331
|
console.log("\n");
|
|
23753
24332
|
} catch (error) {
|
|
23754
24333
|
console.log(
|
|
@@ -23762,7 +24341,7 @@ async function interactiveChatSession(session, debug) {
|
|
|
23762
24341
|
};
|
|
23763
24342
|
rl.on("close", () => {
|
|
23764
24343
|
console.log("\nGoodbye!");
|
|
23765
|
-
|
|
24344
|
+
resolve2();
|
|
23766
24345
|
});
|
|
23767
24346
|
prompt();
|
|
23768
24347
|
});
|
|
@@ -23859,8 +24438,8 @@ async function realtimeChatSession(session, vad, debug) {
|
|
|
23859
24438
|
});
|
|
23860
24439
|
tui.loadHistory(history);
|
|
23861
24440
|
tui.start();
|
|
23862
|
-
await new Promise((
|
|
23863
|
-
client.once("closed",
|
|
24441
|
+
await new Promise((resolve2) => {
|
|
24442
|
+
client.once("closed", resolve2);
|
|
23864
24443
|
});
|
|
23865
24444
|
}
|
|
23866
24445
|
async function voiceChatSession(session, vad, args, debug) {
|
|
@@ -23977,8 +24556,8 @@ Connecting to ${session.label.toLowerCase()}...
|
|
|
23977
24556
|
tui.loadHistory(history);
|
|
23978
24557
|
tui.start();
|
|
23979
24558
|
mic.start(inputDeviceId);
|
|
23980
|
-
await new Promise((
|
|
23981
|
-
client.once("closed",
|
|
24559
|
+
await new Promise((resolve2) => {
|
|
24560
|
+
client.once("closed", resolve2);
|
|
23982
24561
|
});
|
|
23983
24562
|
}
|
|
23984
24563
|
async function listAudioDevices() {
|
|
@@ -24008,13 +24587,14 @@ async function conciergeCommand(hctx) {
|
|
|
24008
24587
|
const chatIdArg = typeof args["chat-id"] === "string" ? args["chat-id"] : void 0;
|
|
24009
24588
|
const message = typeof args.message === "string" ? args.message : void 0;
|
|
24010
24589
|
const chat = await getOrCreateConciergeChat(ctx, chatIdArg, !!message);
|
|
24011
|
-
const session = buildConciergeSession(ctx, chat);
|
|
24590
|
+
const session = buildConciergeSession(ctx, chat, args);
|
|
24012
24591
|
await runChatSession(session, args);
|
|
24013
24592
|
}
|
|
24014
|
-
function buildConciergeSession(ctx, chat) {
|
|
24593
|
+
function buildConciergeSession(ctx, chat, args) {
|
|
24015
24594
|
const wsBase = ctx.envConfig.apiUrl.replace(/^https:/, "wss:").replace(/^http:/, "ws:");
|
|
24016
24595
|
return {
|
|
24017
24596
|
ctx,
|
|
24597
|
+
workspaceUuid: "",
|
|
24018
24598
|
chatId: chat.id,
|
|
24019
24599
|
chatUuid: chat.uuid,
|
|
24020
24600
|
chatTitle: chat.title || chat.uuid,
|
|
@@ -24022,8 +24602,12 @@ function buildConciergeSession(ctx, chat) {
|
|
|
24022
24602
|
wsUrl: `${wsBase}/api/v2/concierge/voice/stream-ws`,
|
|
24023
24603
|
loadMessages: () => getConciergeChatMessages(ctx, chat.id).catch(() => []),
|
|
24024
24604
|
label: "Concierge",
|
|
24025
|
-
model: void 0
|
|
24605
|
+
model: void 0,
|
|
24026
24606
|
// concierge doesn't send model field
|
|
24607
|
+
appContext: buildCliConciergeAppContext(),
|
|
24608
|
+
allowWrite: true,
|
|
24609
|
+
trustCwd: !!args["trust-cwd"],
|
|
24610
|
+
trustDir: typeof args["trust-dir"] === "string" ? args["trust-dir"] : void 0
|
|
24027
24611
|
};
|
|
24028
24612
|
}
|
|
24029
24613
|
async function getOrCreateConciergeChat(ctx, chatIdArg, skipSelection) {
|
|
@@ -24058,6 +24642,95 @@ async function getOrCreateConciergeChat(ctx, chatIdArg, skipSelection) {
|
|
|
24058
24642
|
}
|
|
24059
24643
|
return selectedChat;
|
|
24060
24644
|
}
|
|
24645
|
+
|
|
24646
|
+
// src/commands/get-started.ts
|
|
24647
|
+
var WEB_SETUP = "https://app.gravityrail.com/setup";
|
|
24648
|
+
var WEB_GET_STARTED = "https://app.gravityrail.com/get-started";
|
|
24649
|
+
var HUMAN_GUIDE = `Gravity Rail \u2014 getting started
|
|
24650
|
+
|
|
24651
|
+
1. Authenticate: gr login --env prod
|
|
24652
|
+
Confirm: gr whoami
|
|
24653
|
+
|
|
24654
|
+
2. Recommended path \u2014 concierge (no workspace required):
|
|
24655
|
+
gr concierge chat --allow-write
|
|
24656
|
+
The concierge can create your org (auto-created on first login), list products,
|
|
24657
|
+
start Stripe checkout, and create your first workspace.
|
|
24658
|
+
|
|
24659
|
+
3. Verify workspaces: gr workspaces list
|
|
24660
|
+
|
|
24661
|
+
4. Web wizards (human-friendly): ${WEB_SETUP} and ${WEB_GET_STARTED}
|
|
24662
|
+
|
|
24663
|
+
Headless boundary: Stripe checkout needs a human in a browser \u2014 the concierge returns
|
|
24664
|
+
a checkout URL; an agent should hand that link to the user, not loop on payment.
|
|
24665
|
+
|
|
24666
|
+
Agent-readable steps: gr get-started --format llm
|
|
24667
|
+
`;
|
|
24668
|
+
var LLM_GUIDE = `# Gravity Rail CLI bootstrap (agent instructions)
|
|
24669
|
+
|
|
24670
|
+
## Preconditions
|
|
24671
|
+
- Install: \`npm install -g @gravity-rail/cli\` or \`npx @gravity-rail/cli\`
|
|
24672
|
+
- Authenticate: \`gr login --env prod\` (interactive OAuth). Verify with \`gr whoami\`.
|
|
24673
|
+
- **Monorepo dev:** from gravity-team with Docker dev up, use \`yarn gr <cmd> --realm superadmin\` (reads \`dev-realms.json\`; no browser). Rebuild: \`yarn workspace @gravity-rail/gr-cli build\`.
|
|
24674
|
+
|
|
24675
|
+
## Important facts
|
|
24676
|
+
- **Agents/CI:** do not leave stdin open on read-only commands; use \`GRAVITY_RAIL_API_KEY\` or redirect stdin from /dev/null. Commands only read stdin when \`--data\`/\`--file\` is passed.
|
|
24677
|
+
- Use \`--env dev|staging|prod\` (default prod) so credentials match the target; \`gr whoami\` shows the active env.
|
|
24678
|
+
- After login, an **organization is created automatically** \u2014 do not create a duplicate org unless \`gr organizations list\` is empty.
|
|
24679
|
+
- **Workspace is required** for workflow/agent/task commands (\`-w <workspace-uuid>\`).
|
|
24680
|
+
- **Concierge does not require \`-w\`** \u2014 use it for first-run setup.
|
|
24681
|
+
|
|
24682
|
+
## Recommended path (concierge-driven)
|
|
24683
|
+
\`\`\`bash
|
|
24684
|
+
gr concierge chat --allow-write --message "Help me set up my first Gravity Rail workspace"
|
|
24685
|
+
\`\`\`
|
|
24686
|
+
Flags for unattended turns:
|
|
24687
|
+
- \`--message\` / \`-m\`: single-turn (non-interactive)
|
|
24688
|
+
- \`--auto-approve\`: skip HITL tool approval where policy allows
|
|
24689
|
+
- \`--allow-write\`: required for create/update tools
|
|
24690
|
+
|
|
24691
|
+
The concierge can: list/create orgs, list org/workspace products, create Stripe checkout sessions, create workspace specs, and \`create_workspace\`.
|
|
24692
|
+
|
|
24693
|
+
## Manual CLI path (if not using concierge)
|
|
24694
|
+
1. \`gr whoami\` \u2014 confirm account
|
|
24695
|
+
2. \`gr organizations list\` \u2014 note org slug/UUID
|
|
24696
|
+
3. \`gr concierge chat\` OR web: ${WEB_GET_STARTED}
|
|
24697
|
+
4. Product purchase: concierge returns Stripe checkout URL \u2192 **human must complete in browser**
|
|
24698
|
+
5. \`gr workspaces list\` \u2014 pick workspace UUID for \`-w\`
|
|
24699
|
+
|
|
24700
|
+
## Support (account-level, no workspace)
|
|
24701
|
+
\`\`\`bash
|
|
24702
|
+
gr support create --type question --title "..." --description "..." --allow-write
|
|
24703
|
+
gr support list
|
|
24704
|
+
\`\`\`
|
|
24705
|
+
|
|
24706
|
+
## Web fallbacks
|
|
24707
|
+
- Concierge / setup: ${WEB_SETUP}
|
|
24708
|
+
- Wizard: ${WEB_GET_STARTED}
|
|
24709
|
+
|
|
24710
|
+
## Headless limits (do not retry forever)
|
|
24711
|
+
- Stripe payment / checkout: requires human browser
|
|
24712
|
+
- OAuth login: requires human browser
|
|
24713
|
+
- Directory trust for future local-file tools: use \`--trust-cwd\` only after user confirms files are safe
|
|
24714
|
+
|
|
24715
|
+
## Local project files (CLI only)
|
|
24716
|
+
After directory trust + \`--allow-write\`, the concierge can use \`read_local_file\`, \`list_local_dir\`, \`write_local_file\` (client-executed via HITL).
|
|
24717
|
+
|
|
24718
|
+
## Verify success
|
|
24719
|
+
\`gr workspaces list\` then \`gr workflows list -w <uuid>\` or \`gr agents list -w <uuid>\`
|
|
24720
|
+
`;
|
|
24721
|
+
async function getStartedCommand(hctx) {
|
|
24722
|
+
const format = hctx.args.format ?? hctx.args.agent;
|
|
24723
|
+
const isLlm = format === "llm" || format === "agent" || hctx.args["format"] === "llm" || !!hctx.args.agent;
|
|
24724
|
+
if (!isLlm) {
|
|
24725
|
+
try {
|
|
24726
|
+
requireAuth(hctx.ctx);
|
|
24727
|
+
} catch {
|
|
24728
|
+
}
|
|
24729
|
+
process.stdout.write(HUMAN_GUIDE);
|
|
24730
|
+
return;
|
|
24731
|
+
}
|
|
24732
|
+
process.stdout.write(LLM_GUIDE);
|
|
24733
|
+
}
|
|
24061
24734
|
async function devImportLocalCommand(ctx, workspace) {
|
|
24062
24735
|
stderr2(`Exporting workspace ${workspace}...`);
|
|
24063
24736
|
const exportData = await ctx.apiClient.exportWorkspace(workspace);
|
|
@@ -24136,8 +24809,17 @@ async function manageCommand(hctx) {
|
|
|
24136
24809
|
const chatIdArg = typeof args["chat-id"] === "string" ? args["chat-id"] : void 0;
|
|
24137
24810
|
const message = typeof args.message === "string" ? args.message : void 0;
|
|
24138
24811
|
const chat = await getOrCreateChat(ctx, workspace, chatIdArg, asMember, "manager", !!message);
|
|
24139
|
-
const session = buildWorkspaceSession(ctx, workspace, chat, "Workspace Manager"
|
|
24812
|
+
const session = buildWorkspaceSession(ctx, workspace, chat, "Workspace Manager", {
|
|
24813
|
+
appContext: buildCliConciergeAppContext(),
|
|
24814
|
+
allowWrite: true,
|
|
24815
|
+
trustCwd: !!args["trust-cwd"],
|
|
24816
|
+
trustDir: typeof args["trust-dir"] === "string" ? args["trust-dir"] : void 0
|
|
24817
|
+
});
|
|
24818
|
+
process.stderr.write(`Chat #${session.chatId}
|
|
24819
|
+
`);
|
|
24140
24820
|
await runChatSession(session, args);
|
|
24821
|
+
process.stderr.write(`Chat #${session.chatId}
|
|
24822
|
+
`);
|
|
24141
24823
|
}
|
|
24142
24824
|
async function chatCommand(hctx) {
|
|
24143
24825
|
const ctx = requireAuth(hctx.ctx);
|
|
@@ -24162,10 +24844,11 @@ async function chatCommand(hctx) {
|
|
|
24162
24844
|
const session = buildWorkspaceSession(ctx, workspace, chat, "Chat");
|
|
24163
24845
|
await runChatSession(session, args);
|
|
24164
24846
|
}
|
|
24165
|
-
function buildWorkspaceSession(ctx, workspace, chat, label) {
|
|
24847
|
+
function buildWorkspaceSession(ctx, workspace, chat, label, extra) {
|
|
24166
24848
|
const wsBase = ctx.envConfig.apiUrl.replace(/^https:/, "wss:").replace(/^http:/, "ws:");
|
|
24167
24849
|
return {
|
|
24168
24850
|
ctx,
|
|
24851
|
+
workspaceUuid: workspace,
|
|
24169
24852
|
chatId: chat.id,
|
|
24170
24853
|
chatUuid: chat.uuid,
|
|
24171
24854
|
chatTitle: chat.title || chat.uuid,
|
|
@@ -24173,7 +24856,8 @@ function buildWorkspaceSession(ctx, workspace, chat, label) {
|
|
|
24173
24856
|
wsUrl: `${wsBase}/api/v2/w/${workspace}/realtime/stream-ws`,
|
|
24174
24857
|
loadMessages: () => ctx.apiClient.getChatMessages(workspace, chat.id, 50).catch(() => []),
|
|
24175
24858
|
label,
|
|
24176
|
-
model: "manager"
|
|
24859
|
+
model: "manager",
|
|
24860
|
+
...extra
|
|
24177
24861
|
};
|
|
24178
24862
|
}
|
|
24179
24863
|
async function getOrCreateChat(ctx, workspace, chatIdArg, asMemberId, chatType, skipSelection = false) {
|
|
@@ -24271,16 +24955,20 @@ var COLUMNS35 = [
|
|
|
24271
24955
|
{ header: "NAME", getValue: (a) => a.name || "", maxWidth: 30 },
|
|
24272
24956
|
{ header: "EMAIL", getValue: (a) => a.email || "", maxWidth: 40 },
|
|
24273
24957
|
{ header: "UUID", getValue: (a) => a.uuid },
|
|
24958
|
+
{ header: "ENV", getValue: () => "" },
|
|
24274
24959
|
{ header: "SUPERUSER", getValue: (a) => a.isSuperuser ? "yes" : "no" }
|
|
24275
24960
|
];
|
|
24276
24961
|
async function whoamiCommand(ctx) {
|
|
24277
24962
|
const account = await ctx.apiClient.getUserAccount();
|
|
24278
|
-
|
|
24963
|
+
const columns = COLUMNS35.map(
|
|
24964
|
+
(col) => col.header === "ENV" ? { ...col, getValue: () => ctx.env } : col
|
|
24965
|
+
);
|
|
24966
|
+
outputSingle(account, ctx, columns);
|
|
24279
24967
|
}
|
|
24280
24968
|
|
|
24281
24969
|
// package.json
|
|
24282
24970
|
var package_default = {
|
|
24283
|
-
version: "0.5.
|
|
24971
|
+
version: "0.5.1"};
|
|
24284
24972
|
|
|
24285
24973
|
// src/gr.ts
|
|
24286
24974
|
var VERSION = package_default.version;
|
|
@@ -24333,6 +25021,15 @@ function createRegistry() {
|
|
|
24333
25021
|
name: "auto-approve",
|
|
24334
25022
|
description: "Auto-approve all tool calls (skip HITL)",
|
|
24335
25023
|
isFlag: true
|
|
25024
|
+
},
|
|
25025
|
+
{
|
|
25026
|
+
name: "trust-cwd",
|
|
25027
|
+
description: "Trust files in the current working directory (non-interactive)",
|
|
25028
|
+
isFlag: true
|
|
25029
|
+
},
|
|
25030
|
+
{
|
|
25031
|
+
name: "trust-dir",
|
|
25032
|
+
description: "Trust files in this directory (non-interactive; absolute path)"
|
|
24336
25033
|
}
|
|
24337
25034
|
],
|
|
24338
25035
|
handler: async (hctx) => {
|
|
@@ -24366,6 +25063,37 @@ function createRegistry() {
|
|
|
24366
25063
|
await chatCommand(hctx);
|
|
24367
25064
|
}
|
|
24368
25065
|
});
|
|
25066
|
+
registry.registerCommand({
|
|
25067
|
+
path: ["get-started"],
|
|
25068
|
+
description: "Bootstrap instructions for new users and LLM agents",
|
|
25069
|
+
requiresAuth: false,
|
|
25070
|
+
args: [
|
|
25071
|
+
{
|
|
25072
|
+
name: "format",
|
|
25073
|
+
description: "Output format: llm (agent-readable) or default (human)"
|
|
25074
|
+
},
|
|
25075
|
+
{
|
|
25076
|
+
name: "agent",
|
|
25077
|
+
description: "Alias for --format llm",
|
|
25078
|
+
isFlag: true
|
|
25079
|
+
}
|
|
25080
|
+
],
|
|
25081
|
+
handler: async (hctx) => {
|
|
25082
|
+
await getStartedCommand(hctx);
|
|
25083
|
+
}
|
|
25084
|
+
});
|
|
25085
|
+
registry.registerCommand({
|
|
25086
|
+
path: ["setup"],
|
|
25087
|
+
description: "Alias for get-started \u2014 bootstrap instructions",
|
|
25088
|
+
requiresAuth: false,
|
|
25089
|
+
args: [
|
|
25090
|
+
{ name: "format", description: "Output format: llm or default" },
|
|
25091
|
+
{ name: "agent", description: "Alias for --format llm", isFlag: true }
|
|
25092
|
+
],
|
|
25093
|
+
handler: async (hctx) => {
|
|
25094
|
+
await getStartedCommand(hctx);
|
|
25095
|
+
}
|
|
25096
|
+
});
|
|
24369
25097
|
registry.registerCommand({
|
|
24370
25098
|
path: ["concierge", "chat"],
|
|
24371
25099
|
description: "Interactive concierge chat (text, realtime, or voice)",
|
|
@@ -24385,6 +25113,15 @@ function createRegistry() {
|
|
|
24385
25113
|
name: "auto-approve",
|
|
24386
25114
|
description: "Auto-approve all tool calls (skip HITL)",
|
|
24387
25115
|
isFlag: true
|
|
25116
|
+
},
|
|
25117
|
+
{
|
|
25118
|
+
name: "trust-cwd",
|
|
25119
|
+
description: "Trust files in the current working directory (non-interactive)",
|
|
25120
|
+
isFlag: true
|
|
25121
|
+
},
|
|
25122
|
+
{
|
|
25123
|
+
name: "trust-dir",
|
|
25124
|
+
description: "Trust files in this directory (non-interactive; absolute path)"
|
|
24388
25125
|
}
|
|
24389
25126
|
],
|
|
24390
25127
|
handler: async (hctx) => {
|
|
@@ -24423,6 +25160,7 @@ function createRegistry() {
|
|
|
24423
25160
|
return registry;
|
|
24424
25161
|
}
|
|
24425
25162
|
function parseGlobalOptions(argv) {
|
|
25163
|
+
let env = "prod";
|
|
24426
25164
|
let apiUrl;
|
|
24427
25165
|
let outputFormat;
|
|
24428
25166
|
let verbose = false;
|
|
@@ -24432,6 +25170,13 @@ function parseGlobalOptions(argv) {
|
|
|
24432
25170
|
let collectingCommand = true;
|
|
24433
25171
|
for (let i = 0; i < argv.length; i++) {
|
|
24434
25172
|
const arg = argv[i];
|
|
25173
|
+
const eqIdx = arg.indexOf("=");
|
|
25174
|
+
const flagPart = eqIdx !== -1 ? arg.slice(0, eqIdx) : arg;
|
|
25175
|
+
const inlineValue = eqIdx !== -1 ? arg.slice(eqIdx + 1) : void 0;
|
|
25176
|
+
if (flagPart === "--env" && (inlineValue ?? argv[i + 1])) {
|
|
25177
|
+
env = inlineValue ?? argv[++i];
|
|
25178
|
+
continue;
|
|
25179
|
+
}
|
|
24435
25180
|
if (arg === "--api-url" && argv[i + 1]) {
|
|
24436
25181
|
apiUrl = argv[++i];
|
|
24437
25182
|
continue;
|
|
@@ -24459,7 +25204,7 @@ function parseGlobalOptions(argv) {
|
|
|
24459
25204
|
rest.unshift(commandWords.pop());
|
|
24460
25205
|
}
|
|
24461
25206
|
return {
|
|
24462
|
-
opts: { apiUrl, outputFormat, verbose, allowWrite },
|
|
25207
|
+
opts: { env, apiUrl, outputFormat, verbose, allowWrite },
|
|
24463
25208
|
commandWords,
|
|
24464
25209
|
rest
|
|
24465
25210
|
};
|
|
@@ -24508,7 +25253,8 @@ async function main() {
|
|
|
24508
25253
|
process.stderr.write("Error: --workspace (-w) is required\n");
|
|
24509
25254
|
process.exit(1);
|
|
24510
25255
|
}
|
|
24511
|
-
const
|
|
25256
|
+
const acceptsJsonInput = cmd.args?.some((a) => a.name === "data" || a.name === "file");
|
|
25257
|
+
const data = acceptsJsonInput ? await readJsonInput(parsedArgs) : void 0;
|
|
24512
25258
|
hctx = { ctx, args: parsedArgs, workspace, data };
|
|
24513
25259
|
} else {
|
|
24514
25260
|
const baseCtx = createPublicContext(opts);
|
|
@@ -24527,7 +25273,7 @@ async function main() {
|
|
|
24527
25273
|
}
|
|
24528
25274
|
await cmd.handler(hctx);
|
|
24529
25275
|
if (process.stdout.writableNeedDrain) {
|
|
24530
|
-
await new Promise((
|
|
25276
|
+
await new Promise((resolve2) => process.stdout.once("drain", resolve2));
|
|
24531
25277
|
}
|
|
24532
25278
|
process.exit(0);
|
|
24533
25279
|
}
|