@salesforce/graphiti 10.10.2
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/AGENT_GUIDE.md +424 -0
- package/CHANGELOG.md +448 -0
- package/LICENSE.txt +82 -0
- package/README.md +204 -0
- package/TASK.md +249 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.js +683 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/args.d.ts +13 -0
- package/dist/commands/args.js +207 -0
- package/dist/commands/args.js.map +1 -0
- package/dist/commands/build.d.ts +11 -0
- package/dist/commands/build.js +209 -0
- package/dist/commands/build.js.map +1 -0
- package/dist/commands/connect.d.ts +8 -0
- package/dist/commands/connect.js +55 -0
- package/dist/commands/connect.js.map +1 -0
- package/dist/commands/describe.d.ts +6 -0
- package/dist/commands/describe.js +229 -0
- package/dist/commands/describe.js.map +1 -0
- package/dist/commands/meta.d.ts +9 -0
- package/dist/commands/meta.js +140 -0
- package/dist/commands/meta.js.map +1 -0
- package/dist/commands/navigate.d.ts +14 -0
- package/dist/commands/navigate.js +105 -0
- package/dist/commands/navigate.js.map +1 -0
- package/dist/commands/query-helpers.d.ts +80 -0
- package/dist/commands/query-helpers.js +865 -0
- package/dist/commands/query-helpers.js.map +1 -0
- package/dist/commands/query.d.ts +26 -0
- package/dist/commands/query.js +901 -0
- package/dist/commands/query.js.map +1 -0
- package/dist/commands/review.d.ts +18 -0
- package/dist/commands/review.js +533 -0
- package/dist/commands/review.js.map +1 -0
- package/dist/commands/session-mgmt.d.ts +25 -0
- package/dist/commands/session-mgmt.js +206 -0
- package/dist/commands/session-mgmt.js.map +1 -0
- package/dist/commands/type.d.ts +6 -0
- package/dist/commands/type.js +342 -0
- package/dist/commands/type.js.map +1 -0
- package/dist/commands/validate-input.d.ts +6 -0
- package/dist/commands/validate-input.js +32 -0
- package/dist/commands/validate-input.js.map +1 -0
- package/dist/intent/build-aggregate.d.ts +33 -0
- package/dist/intent/build-aggregate.js +134 -0
- package/dist/intent/build-aggregate.js.map +1 -0
- package/dist/intent/build-create.d.ts +14 -0
- package/dist/intent/build-create.js +16 -0
- package/dist/intent/build-create.js.map +1 -0
- package/dist/intent/build-delete.d.ts +30 -0
- package/dist/intent/build-delete.js +53 -0
- package/dist/intent/build-delete.js.map +1 -0
- package/dist/intent/build-detail.d.ts +32 -0
- package/dist/intent/build-detail.js +80 -0
- package/dist/intent/build-detail.js.map +1 -0
- package/dist/intent/build-discover.d.ts +30 -0
- package/dist/intent/build-discover.js +149 -0
- package/dist/intent/build-discover.js.map +1 -0
- package/dist/intent/build-list.d.ts +28 -0
- package/dist/intent/build-list.js +75 -0
- package/dist/intent/build-list.js.map +1 -0
- package/dist/intent/build-mutation.d.ts +23 -0
- package/dist/intent/build-mutation.js +54 -0
- package/dist/intent/build-mutation.js.map +1 -0
- package/dist/intent/build-output.d.ts +27 -0
- package/dist/intent/build-output.js +60 -0
- package/dist/intent/build-output.js.map +1 -0
- package/dist/intent/build-raw.d.ts +23 -0
- package/dist/intent/build-raw.js +54 -0
- package/dist/intent/build-raw.js.map +1 -0
- package/dist/intent/build-update.d.ts +14 -0
- package/dist/intent/build-update.js +16 -0
- package/dist/intent/build-update.js.map +1 -0
- package/dist/intent/get-schema-with-priming.d.ts +26 -0
- package/dist/intent/get-schema-with-priming.js +32 -0
- package/dist/intent/get-schema-with-priming.js.map +1 -0
- package/dist/intent/select-child-relationship.d.ts +19 -0
- package/dist/intent/select-child-relationship.js +38 -0
- package/dist/intent/select-child-relationship.js.map +1 -0
- package/dist/intent/types.d.ts +159 -0
- package/dist/intent/types.js +21 -0
- package/dist/intent/types.js.map +1 -0
- package/dist/lib/apply-command.d.ts +15 -0
- package/dist/lib/apply-command.js +238 -0
- package/dist/lib/apply-command.js.map +1 -0
- package/dist/lib/auth.d.ts +38 -0
- package/dist/lib/auth.js +113 -0
- package/dist/lib/auth.js.map +1 -0
- package/dist/lib/codegen.d.ts +32 -0
- package/dist/lib/codegen.js +700 -0
- package/dist/lib/codegen.js.map +1 -0
- package/dist/lib/command-registry.d.ts +59 -0
- package/dist/lib/command-registry.js +366 -0
- package/dist/lib/command-registry.js.map +1 -0
- package/dist/lib/formatter.d.ts +76 -0
- package/dist/lib/formatter.js +419 -0
- package/dist/lib/formatter.js.map +1 -0
- package/dist/lib/fs-utils.d.ts +23 -0
- package/dist/lib/fs-utils.js +46 -0
- package/dist/lib/fs-utils.js.map +1 -0
- package/dist/lib/graphql-name.d.ts +27 -0
- package/dist/lib/graphql-name.js +32 -0
- package/dist/lib/graphql-name.js.map +1 -0
- package/dist/lib/interactive.d.ts +6 -0
- package/dist/lib/interactive.js +562 -0
- package/dist/lib/interactive.js.map +1 -0
- package/dist/lib/introspect.d.ts +40 -0
- package/dist/lib/introspect.js +280 -0
- package/dist/lib/introspect.js.map +1 -0
- package/dist/lib/object-info.d.ts +79 -0
- package/dist/lib/object-info.js +313 -0
- package/dist/lib/object-info.js.map +1 -0
- package/dist/lib/path-selection.d.ts +50 -0
- package/dist/lib/path-selection.js +146 -0
- package/dist/lib/path-selection.js.map +1 -0
- package/dist/lib/prime-schema.d.ts +59 -0
- package/dist/lib/prime-schema.js +158 -0
- package/dist/lib/prime-schema.js.map +1 -0
- package/dist/lib/query-builder.d.ts +10 -0
- package/dist/lib/query-builder.js +168 -0
- package/dist/lib/query-builder.js.map +1 -0
- package/dist/lib/query-commands.d.ts +19 -0
- package/dist/lib/query-commands.js +262 -0
- package/dist/lib/query-commands.js.map +1 -0
- package/dist/lib/session.d.ts +205 -0
- package/dist/lib/session.js +1075 -0
- package/dist/lib/session.js.map +1 -0
- package/dist/lib/tokenize.d.ts +12 -0
- package/dist/lib/tokenize.js +48 -0
- package/dist/lib/tokenize.js.map +1 -0
- package/dist/lib/uiapi.d.ts +109 -0
- package/dist/lib/uiapi.js +159 -0
- package/dist/lib/uiapi.js.map +1 -0
- package/dist/lib/validator.d.ts +27 -0
- package/dist/lib/validator.js +100 -0
- package/dist/lib/validator.js.map +1 -0
- package/dist/lib/variable-promotion.d.ts +69 -0
- package/dist/lib/variable-promotion.js +95 -0
- package/dist/lib/variable-promotion.js.map +1 -0
- package/dist/lib/walker.d.ts +147 -0
- package/dist/lib/walker.js +723 -0
- package/dist/lib/walker.js.map +1 -0
- package/dist/mcp/server.d.ts +7 -0
- package/dist/mcp/server.js +34 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/stdio.d.ts +7 -0
- package/dist/mcp/stdio.js +25 -0
- package/dist/mcp/stdio.js.map +1 -0
- package/dist/mcp/tools/echo.d.ts +7 -0
- package/dist/mcp/tools/echo.js +17 -0
- package/dist/mcp/tools/echo.js.map +1 -0
- package/dist/mcp/tools/sf-gql-aggregate.d.ts +11 -0
- package/dist/mcp/tools/sf-gql-aggregate.js +75 -0
- package/dist/mcp/tools/sf-gql-aggregate.js.map +1 -0
- package/dist/mcp/tools/sf-gql-create.d.ts +11 -0
- package/dist/mcp/tools/sf-gql-create.js +35 -0
- package/dist/mcp/tools/sf-gql-create.js.map +1 -0
- package/dist/mcp/tools/sf-gql-delete.d.ts +11 -0
- package/dist/mcp/tools/sf-gql-delete.js +31 -0
- package/dist/mcp/tools/sf-gql-delete.js.map +1 -0
- package/dist/mcp/tools/sf-gql-detail.d.ts +11 -0
- package/dist/mcp/tools/sf-gql-detail.js +58 -0
- package/dist/mcp/tools/sf-gql-detail.js.map +1 -0
- package/dist/mcp/tools/sf-gql-discover.d.ts +9 -0
- package/dist/mcp/tools/sf-gql-discover.js +51 -0
- package/dist/mcp/tools/sf-gql-discover.js.map +1 -0
- package/dist/mcp/tools/sf-gql-list.d.ts +11 -0
- package/dist/mcp/tools/sf-gql-list.js +53 -0
- package/dist/mcp/tools/sf-gql-list.js.map +1 -0
- package/dist/mcp/tools/sf-gql-raw.d.ts +11 -0
- package/dist/mcp/tools/sf-gql-raw.js +39 -0
- package/dist/mcp/tools/sf-gql-raw.js.map +1 -0
- package/dist/mcp/tools/sf-gql-update.d.ts +11 -0
- package/dist/mcp/tools/sf-gql-update.js +35 -0
- package/dist/mcp/tools/sf-gql-update.js.map +1 -0
- package/dist/mcp/tools/shared/zod-schemas.d.ts +49 -0
- package/dist/mcp/tools/shared/zod-schemas.js +46 -0
- package/dist/mcp/tools/shared/zod-schemas.js.map +1 -0
- package/package.json +36 -0
- package/ralph-loop.sh +120 -0
- package/scripts/smoke-orderby.sh +190 -0
- package/src/__tests__/helpers/prime-deps.ts +46 -0
- package/src/__tests__/helpers/schema.ts +73 -0
- package/src/__tests__/helpers/stdout.ts +33 -0
- package/src/__tests__/setup.ts +19 -0
- package/src/cli.ts +764 -0
- package/src/commands/__tests__/query.spec.ts +137 -0
- package/src/commands/args.ts +306 -0
- package/src/commands/build.ts +288 -0
- package/src/commands/connect.ts +60 -0
- package/src/commands/describe.ts +246 -0
- package/src/commands/meta.ts +171 -0
- package/src/commands/navigate.ts +134 -0
- package/src/commands/query-helpers.ts +1202 -0
- package/src/commands/query.ts +1085 -0
- package/src/commands/review.ts +670 -0
- package/src/commands/session-mgmt.ts +256 -0
- package/src/commands/type.ts +437 -0
- package/src/commands/validate-input.ts +38 -0
- package/src/intent/__tests__/build-aggregate.spec.ts +931 -0
- package/src/intent/__tests__/build-create-validation.spec.ts +135 -0
- package/src/intent/__tests__/build-delete.spec.ts +121 -0
- package/src/intent/__tests__/build-detail.spec.ts +333 -0
- package/src/intent/__tests__/build-discover.spec.ts +432 -0
- package/src/intent/__tests__/build-list.spec.ts +284 -0
- package/src/intent/__tests__/build-mutation.spec.ts +108 -0
- package/src/intent/__tests__/build-output.spec.ts +55 -0
- package/src/intent/__tests__/build-raw.spec.ts +153 -0
- package/src/intent/__tests__/build-update-validation.spec.ts +134 -0
- package/src/intent/build-aggregate.ts +182 -0
- package/src/intent/build-create.ts +19 -0
- package/src/intent/build-delete.ts +62 -0
- package/src/intent/build-detail.ts +95 -0
- package/src/intent/build-discover.ts +199 -0
- package/src/intent/build-list.ts +91 -0
- package/src/intent/build-mutation.ts +75 -0
- package/src/intent/build-output.ts +72 -0
- package/src/intent/build-raw.ts +61 -0
- package/src/intent/build-update.ts +19 -0
- package/src/intent/get-schema-with-priming.ts +43 -0
- package/src/intent/select-child-relationship.ts +48 -0
- package/src/intent/types.ts +181 -0
- package/src/lib/__tests__/apply-command.parity.spec.ts +97 -0
- package/src/lib/__tests__/apply-command.spec.ts +171 -0
- package/src/lib/__tests__/auth.spec.ts +292 -0
- package/src/lib/__tests__/formatter.spec.ts +86 -0
- package/src/lib/__tests__/graphql-name.spec.ts +64 -0
- package/src/lib/__tests__/introspect.spec.ts +399 -0
- package/src/lib/__tests__/object-info.spec.ts +124 -0
- package/src/lib/__tests__/path-selection.spec.ts +219 -0
- package/src/lib/__tests__/prime-schema.spec.ts +269 -0
- package/src/lib/__tests__/query-builder.spec.ts +95 -0
- package/src/lib/__tests__/query-commands.spec.ts +74 -0
- package/src/lib/__tests__/session.spec.ts +292 -0
- package/src/lib/__tests__/tokenize.spec.ts +33 -0
- package/src/lib/__tests__/uiapi.spec.ts +192 -0
- package/src/lib/__tests__/variable-promotion.spec.ts +211 -0
- package/src/lib/__tests__/walker.spec.ts +250 -0
- package/src/lib/apply-command.ts +286 -0
- package/src/lib/auth.ts +157 -0
- package/src/lib/codegen.ts +899 -0
- package/src/lib/command-registry.ts +434 -0
- package/src/lib/formatter.ts +587 -0
- package/src/lib/fs-utils.ts +46 -0
- package/src/lib/graphql-name.ts +35 -0
- package/src/lib/interactive.ts +634 -0
- package/src/lib/introspect.ts +320 -0
- package/src/lib/object-info.ts +409 -0
- package/src/lib/path-selection.ts +162 -0
- package/src/lib/prime-schema.ts +195 -0
- package/src/lib/query-builder.ts +193 -0
- package/src/lib/query-commands.ts +290 -0
- package/src/lib/session.ts +1304 -0
- package/src/lib/tokenize.ts +43 -0
- package/src/lib/uiapi.ts +176 -0
- package/src/lib/validator.ts +143 -0
- package/src/lib/variable-promotion.ts +145 -0
- package/src/lib/walker.ts +975 -0
- package/src/mcp/__tests__/server.spec.ts +155 -0
- package/src/mcp/server.ts +38 -0
- package/src/mcp/stdio.ts +29 -0
- package/src/mcp/tools/__tests__/sf-gql-aggregate.spec.ts +173 -0
- package/src/mcp/tools/__tests__/sf-gql-create.spec.ts +235 -0
- package/src/mcp/tools/__tests__/sf-gql-delete.spec.ts +194 -0
- package/src/mcp/tools/__tests__/sf-gql-detail.spec.ts +246 -0
- package/src/mcp/tools/__tests__/sf-gql-discover.spec.ts +320 -0
- package/src/mcp/tools/__tests__/sf-gql-list.spec.ts +128 -0
- package/src/mcp/tools/__tests__/sf-gql-raw.spec.ts +141 -0
- package/src/mcp/tools/__tests__/sf-gql-update.spec.ts +207 -0
- package/src/mcp/tools/echo.ts +24 -0
- package/src/mcp/tools/sf-gql-aggregate.ts +102 -0
- package/src/mcp/tools/sf-gql-create.ts +55 -0
- package/src/mcp/tools/sf-gql-delete.ts +49 -0
- package/src/mcp/tools/sf-gql-detail.ts +85 -0
- package/src/mcp/tools/sf-gql-discover.ts +67 -0
- package/src/mcp/tools/sf-gql-list.ts +73 -0
- package/src/mcp/tools/sf-gql-raw.ts +56 -0
- package/src/mcp/tools/sf-gql-update.ts +55 -0
- package/src/mcp/tools/shared/zod-schemas.ts +55 -0
- package/tsconfig.json +18 -0
- package/vitest.config.ts +14 -0
|
@@ -0,0 +1,634 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026, Salesforce, Inc.,
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* For full license text, see the LICENSE.txt file
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as readline from "readline";
|
|
8
|
+
import { setInteractiveMode } from "./query-commands.js";
|
|
9
|
+
import {
|
|
10
|
+
loadSession,
|
|
11
|
+
getNavigationContext,
|
|
12
|
+
isInArgsContext,
|
|
13
|
+
getArgsFieldPath,
|
|
14
|
+
getInputSubPath,
|
|
15
|
+
queryNavToSchemaPath,
|
|
16
|
+
parseVariablePath,
|
|
17
|
+
type QuerySession,
|
|
18
|
+
} from "./session.js";
|
|
19
|
+
import { formatPath } from "./session.js";
|
|
20
|
+
import {
|
|
21
|
+
resolvePath,
|
|
22
|
+
getSchema,
|
|
23
|
+
getRootFields,
|
|
24
|
+
resolveInputPath,
|
|
25
|
+
resolveArgByName,
|
|
26
|
+
} from "./walker.js";
|
|
27
|
+
import { dispatchQueryCommand, queryExecute, CommandError } from "../commands/query.js";
|
|
28
|
+
|
|
29
|
+
// ── Private helpers ───────────────────────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
function requireInstanceUrl(session: QuerySession): string {
|
|
32
|
+
if (!session.instanceUrl) {
|
|
33
|
+
throw new CommandError(
|
|
34
|
+
`Session ${session.id} has no instanceUrl. Recreate it with \`graphiti query new <org>\`.`,
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
return session.instanceUrl;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// ── Prompt helpers ────────────────────────────────────────────────────────────
|
|
41
|
+
|
|
42
|
+
function ask(rl: readline.Interface, question: string): Promise<string> {
|
|
43
|
+
return new Promise((resolve) => rl.question(question, (answer) => resolve(answer.trim())));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function pickFromList(
|
|
47
|
+
rl: readline.Interface,
|
|
48
|
+
prompt: string,
|
|
49
|
+
items: string[],
|
|
50
|
+
): Promise<string | null> {
|
|
51
|
+
return new Promise((resolve) => {
|
|
52
|
+
if (items.length === 0) {
|
|
53
|
+
console.log("(no items available)");
|
|
54
|
+
resolve(null);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
console.log(prompt);
|
|
59
|
+
items.forEach((item, i) => console.log(` ${i + 1}. ${item}`));
|
|
60
|
+
rl.question(`Enter number or name [1-${items.length}]: `, (answer) => {
|
|
61
|
+
const trimmed = answer.trim();
|
|
62
|
+
const asNum = parseInt(trimmed, 10);
|
|
63
|
+
if (!isNaN(asNum) && asNum >= 1 && asNum <= items.length) {
|
|
64
|
+
resolve(items[asNum - 1]);
|
|
65
|
+
} else if (items.includes(trimmed)) {
|
|
66
|
+
resolve(trimmed);
|
|
67
|
+
} else if (trimmed === "") {
|
|
68
|
+
resolve(null);
|
|
69
|
+
} else {
|
|
70
|
+
console.log(`"${trimmed}" is not a valid choice.`);
|
|
71
|
+
resolve(null);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ── Guided prompt flows ───────────────────────────────────────────────────────
|
|
78
|
+
|
|
79
|
+
async function guidedSelect(sessionId: string, rl: readline.Interface): Promise<void> {
|
|
80
|
+
const session = loadSession(sessionId);
|
|
81
|
+
const schema = getSchema(requireInstanceUrl(session));
|
|
82
|
+
const schemaPath = queryNavToSchemaPath(session.navigationPath);
|
|
83
|
+
if (schemaPath.length === 0) {
|
|
84
|
+
console.log("Navigate into a query field first (e.g. `cd query/uiapi/query/Account`).");
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const wr = resolvePath(schema, session.operation, schemaPath);
|
|
88
|
+
|
|
89
|
+
if (wr.isLeaf || wr.fields.length === 0) {
|
|
90
|
+
console.log("No leaf fields available at the current path. Navigate into a directory first.");
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const leaves = wr.fields.filter((f) => f.typeKind === "SCALAR" || f.typeKind === "ENUM");
|
|
95
|
+
if (leaves.length === 0) {
|
|
96
|
+
console.log(
|
|
97
|
+
"No leaf fields found here. All fields are directories — use `cd` to navigate deeper.",
|
|
98
|
+
);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const chosen = await pickFromList(
|
|
103
|
+
rl,
|
|
104
|
+
"Available leaf fields:",
|
|
105
|
+
leaves.map((f) => `${f.name} (${f.typeName})`),
|
|
106
|
+
);
|
|
107
|
+
if (!chosen) return;
|
|
108
|
+
|
|
109
|
+
const leafName = chosen.split(" ")[0];
|
|
110
|
+
const alias = await ask(rl, `Alias for "${leafName}" (leave blank for none): `);
|
|
111
|
+
|
|
112
|
+
await dispatchQueryCommand(sessionId, "select", [leafName], { as: alias || undefined });
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async function guidedCd(sessionId: string, rl: readline.Interface): Promise<void> {
|
|
116
|
+
const session = loadSession(sessionId);
|
|
117
|
+
const ctx = getNavigationContext(session.navigationPath);
|
|
118
|
+
|
|
119
|
+
const navItems: string[] = [];
|
|
120
|
+
|
|
121
|
+
if (session.navigationPath.length === 0) {
|
|
122
|
+
navItems.push("query/", "variables/");
|
|
123
|
+
} else if (ctx === "variables") {
|
|
124
|
+
if (session.navigationPath.length > 1) navItems.push("..");
|
|
125
|
+
for (const v of session.variables) {
|
|
126
|
+
navItems.push(`$${v.name}/`);
|
|
127
|
+
}
|
|
128
|
+
} else if (isInArgsContext(session.navigationPath)) {
|
|
129
|
+
navItems.push("..");
|
|
130
|
+
const schema = getSchema(requireInstanceUrl(session));
|
|
131
|
+
const fieldSchemaPath = getArgsFieldPath(session.navigationPath);
|
|
132
|
+
const inputSubPath = getInputSubPath(session.navigationPath);
|
|
133
|
+
|
|
134
|
+
if (inputSubPath.length === 0) {
|
|
135
|
+
const wr = resolvePath(schema, session.operation, fieldSchemaPath);
|
|
136
|
+
for (const arg of wr.args) {
|
|
137
|
+
const isLeaf = arg.typeKind === "SCALAR" || arg.typeKind === "ENUM";
|
|
138
|
+
if (!isLeaf) navItems.push(`${arg.name}/`);
|
|
139
|
+
}
|
|
140
|
+
} else {
|
|
141
|
+
const argName = inputSubPath[0];
|
|
142
|
+
const wr = resolvePath(schema, session.operation, fieldSchemaPath);
|
|
143
|
+
const argInfo = resolveArgByName(schema, wr, argName);
|
|
144
|
+
const remaining = inputSubPath.slice(1);
|
|
145
|
+
const inputResult = resolveInputPath(schema, argInfo.typeName, remaining);
|
|
146
|
+
for (const f of inputResult.inputFields) {
|
|
147
|
+
const isLeaf = f.typeKind === "SCALAR" || f.typeKind === "ENUM";
|
|
148
|
+
if (!isLeaf) navItems.push(`${f.name}/`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
} else {
|
|
152
|
+
const schema = getSchema(requireInstanceUrl(session));
|
|
153
|
+
const schemaPath = queryNavToSchemaPath(session.navigationPath);
|
|
154
|
+
|
|
155
|
+
if (session.navigationPath.length > 0) navItems.push("..");
|
|
156
|
+
|
|
157
|
+
if (schemaPath.length === 0) {
|
|
158
|
+
const rootFields = getRootFields(schema, session.operation);
|
|
159
|
+
navItems.push(...rootFields.map((f) => `${f.name}/`));
|
|
160
|
+
} else {
|
|
161
|
+
const wr = resolvePath(schema, session.operation, schemaPath);
|
|
162
|
+
if (wr.args.length > 0) navItems.push("@args/");
|
|
163
|
+
const dirs = wr.fields
|
|
164
|
+
.filter((f) => f.typeKind !== "SCALAR" && f.typeKind !== "ENUM")
|
|
165
|
+
.map((f) => `${f.name}/`);
|
|
166
|
+
const frags = wr.possibleTypes.map((t) => `[${t}]/`);
|
|
167
|
+
navItems.push(...dirs, ...frags);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (navItems.length === 0) {
|
|
172
|
+
console.log("No navigable directories at the current path.");
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const chosen = await pickFromList(rl, "Navigate to:", navItems);
|
|
177
|
+
if (!chosen) return;
|
|
178
|
+
|
|
179
|
+
const target = chosen.replace(/\/$/, "");
|
|
180
|
+
await dispatchQueryCommand(sessionId, "cd", [target], {});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async function guidedAssign(sessionId: string, rl: readline.Interface): Promise<void> {
|
|
184
|
+
const session = loadSession(sessionId);
|
|
185
|
+
const ctx = getNavigationContext(session.navigationPath);
|
|
186
|
+
|
|
187
|
+
if (ctx === "variables") {
|
|
188
|
+
const varParsed = parseVariablePath(session.navigationPath);
|
|
189
|
+
if (!varParsed) {
|
|
190
|
+
console.log("Navigate into a variable first (e.g. `cd $myVar`), then use `assign`.");
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
const variable = session.variables.find((v) => v.name === varParsed.varName);
|
|
194
|
+
if (!variable) {
|
|
195
|
+
console.log(`Variable "$${varParsed.varName}" is not defined.`);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const schema = getSchema(requireInstanceUrl(session));
|
|
200
|
+
const rawType = variable.type.replace(/[![\]]/g, "");
|
|
201
|
+
const inputResult = resolveInputPath(schema, rawType, varParsed.inputSubPath);
|
|
202
|
+
|
|
203
|
+
const leaves = inputResult.inputFields.filter(
|
|
204
|
+
(f) => f.typeKind === "SCALAR" || f.typeKind === "ENUM",
|
|
205
|
+
);
|
|
206
|
+
if (leaves.length === 0) {
|
|
207
|
+
console.log("No assignable fields here.");
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const chosen = await pickFromList(
|
|
212
|
+
rl,
|
|
213
|
+
"Assign to:",
|
|
214
|
+
leaves.map((f) => `${f.name}: ${f.typeName}`),
|
|
215
|
+
);
|
|
216
|
+
if (!chosen) return;
|
|
217
|
+
const fieldName = chosen.split(":")[0].trim();
|
|
218
|
+
const value = await ask(rl, `Value for ${fieldName}: `);
|
|
219
|
+
if (!value) return;
|
|
220
|
+
await dispatchQueryCommand(sessionId, "set", [fieldName, value], {});
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (!isInArgsContext(session.navigationPath)) {
|
|
225
|
+
console.log("Navigate into `@args/` first, then use `set`.");
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const schema = getSchema(requireInstanceUrl(session));
|
|
230
|
+
const fieldSchemaPath = getArgsFieldPath(session.navigationPath);
|
|
231
|
+
const inputSubPath = getInputSubPath(session.navigationPath);
|
|
232
|
+
|
|
233
|
+
if (inputSubPath.length === 0) {
|
|
234
|
+
const wr = resolvePath(schema, session.operation, fieldSchemaPath);
|
|
235
|
+
const leaves = wr.args.filter((a) => a.typeKind === "SCALAR" || a.typeKind === "ENUM");
|
|
236
|
+
if (leaves.length === 0) {
|
|
237
|
+
console.log("No scalar args here. Navigate deeper.");
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
const chosen = await pickFromList(
|
|
241
|
+
rl,
|
|
242
|
+
"Assign to:",
|
|
243
|
+
leaves.map((a) => `${a.name}: ${a.typeName}`),
|
|
244
|
+
);
|
|
245
|
+
if (!chosen) return;
|
|
246
|
+
const argName = chosen.split(":")[0].trim();
|
|
247
|
+
const value = await ask(rl, `Value for ${argName}: `);
|
|
248
|
+
if (!value) return;
|
|
249
|
+
await dispatchQueryCommand(sessionId, "set", [argName, value], {});
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const argName = inputSubPath[0];
|
|
254
|
+
const wr = resolvePath(schema, session.operation, fieldSchemaPath);
|
|
255
|
+
const argInfo = resolveArgByName(schema, wr, argName);
|
|
256
|
+
const remaining = inputSubPath.slice(1);
|
|
257
|
+
const inputResult = resolveInputPath(schema, argInfo.typeName, remaining);
|
|
258
|
+
|
|
259
|
+
const leaves = inputResult.inputFields.filter(
|
|
260
|
+
(f) => f.typeKind === "SCALAR" || f.typeKind === "ENUM",
|
|
261
|
+
);
|
|
262
|
+
if (leaves.length === 0) {
|
|
263
|
+
console.log("No assignable fields here.");
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const chosen = await pickFromList(
|
|
268
|
+
rl,
|
|
269
|
+
"Assign to:",
|
|
270
|
+
leaves.map((f) => `${f.name}: ${f.typeName}`),
|
|
271
|
+
);
|
|
272
|
+
if (!chosen) return;
|
|
273
|
+
const fieldName = chosen.split(":")[0].trim();
|
|
274
|
+
const value = await ask(rl, `Value for ${fieldName}: `);
|
|
275
|
+
if (!value) return;
|
|
276
|
+
await dispatchQueryCommand(sessionId, "set", [fieldName, value], {});
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
async function guidedExecute(sessionId: string, rl: readline.Interface): Promise<void> {
|
|
280
|
+
const session = loadSession(sessionId);
|
|
281
|
+
const vars = session.variables;
|
|
282
|
+
|
|
283
|
+
const overrides: Record<string, string> = {};
|
|
284
|
+
|
|
285
|
+
if (vars.length > 0) {
|
|
286
|
+
console.log("Set variable values (Enter to keep the current value):");
|
|
287
|
+
for (const variable of vars) {
|
|
288
|
+
const current = variable.runtimeValue ?? variable.defaultValue;
|
|
289
|
+
const hint = current !== undefined ? ` [${current}]` : " (unset)";
|
|
290
|
+
const raw = await ask(rl, ` $${variable.name}: ${variable.type}${hint} = `);
|
|
291
|
+
const trimmed = raw.trim();
|
|
292
|
+
if (trimmed) {
|
|
293
|
+
overrides[variable.name] = trimmed;
|
|
294
|
+
} else if (current !== undefined) {
|
|
295
|
+
overrides[variable.name] = current;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
await queryExecute(sessionId, Object.keys(overrides).length > 0 ? overrides : undefined);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// ── Tokenizer for REPL input ──────────────────────────────────────────────────
|
|
304
|
+
|
|
305
|
+
function tokenize(input: string): string[] {
|
|
306
|
+
const tokens: string[] = [];
|
|
307
|
+
let current = "";
|
|
308
|
+
let inQuote: string | null = null;
|
|
309
|
+
|
|
310
|
+
for (const ch of input) {
|
|
311
|
+
if (inQuote) {
|
|
312
|
+
if (ch === inQuote) {
|
|
313
|
+
inQuote = null;
|
|
314
|
+
} else {
|
|
315
|
+
current += ch;
|
|
316
|
+
}
|
|
317
|
+
} else if (ch === '"' || ch === "'") {
|
|
318
|
+
inQuote = ch;
|
|
319
|
+
} else if (ch === " " || ch === "\t") {
|
|
320
|
+
if (current.length > 0) {
|
|
321
|
+
tokens.push(current);
|
|
322
|
+
current = "";
|
|
323
|
+
}
|
|
324
|
+
} else {
|
|
325
|
+
current += ch;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
if (current.length > 0) tokens.push(current);
|
|
329
|
+
return tokens;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// ── Tab completion ────────────────────────────────────────────────────────────
|
|
333
|
+
|
|
334
|
+
const TOP_LEVEL_COMMANDS = [
|
|
335
|
+
"pwd",
|
|
336
|
+
"ls",
|
|
337
|
+
"cd",
|
|
338
|
+
"select",
|
|
339
|
+
"drop",
|
|
340
|
+
"reset",
|
|
341
|
+
"set",
|
|
342
|
+
"unset",
|
|
343
|
+
"alias",
|
|
344
|
+
"var",
|
|
345
|
+
"optional",
|
|
346
|
+
"describe",
|
|
347
|
+
"show",
|
|
348
|
+
"check",
|
|
349
|
+
"run",
|
|
350
|
+
"codegen",
|
|
351
|
+
"help",
|
|
352
|
+
"sessions",
|
|
353
|
+
"undo",
|
|
354
|
+
"clone",
|
|
355
|
+
"exit",
|
|
356
|
+
];
|
|
357
|
+
|
|
358
|
+
function listDirsAtPath(session: QuerySession, navPath: string[]): string[] {
|
|
359
|
+
try {
|
|
360
|
+
const ctx = getNavigationContext(navPath);
|
|
361
|
+
const schema = getSchema(requireInstanceUrl(session));
|
|
362
|
+
|
|
363
|
+
if (navPath.length === 0) {
|
|
364
|
+
return ["query/", "variables/"];
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
if (ctx === "variables") {
|
|
368
|
+
const varParsed = parseVariablePath(navPath);
|
|
369
|
+
if (!varParsed) {
|
|
370
|
+
return session.variables.map((v) => `$${v.name}/`);
|
|
371
|
+
}
|
|
372
|
+
const variable = session.variables.find((v) => v.name === varParsed.varName);
|
|
373
|
+
if (!variable) return [];
|
|
374
|
+
const rawType = variable.type.replace(/[![\]]/g, "");
|
|
375
|
+
const inputResult = resolveInputPath(schema, rawType, varParsed.inputSubPath);
|
|
376
|
+
return inputResult.inputFields
|
|
377
|
+
.filter((f) => f.typeKind !== "SCALAR" && f.typeKind !== "ENUM")
|
|
378
|
+
.map((f) => `${f.name}/`);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
if (isInArgsContext(navPath)) {
|
|
382
|
+
const fieldSchemaPath = getArgsFieldPath(navPath);
|
|
383
|
+
const inputSubPath = getInputSubPath(navPath);
|
|
384
|
+
if (inputSubPath.length === 0) {
|
|
385
|
+
const wr = resolvePath(schema, session.operation, fieldSchemaPath);
|
|
386
|
+
return wr.args
|
|
387
|
+
.filter((a) => a.typeKind !== "SCALAR" && a.typeKind !== "ENUM")
|
|
388
|
+
.map((a) => `${a.name}/`);
|
|
389
|
+
}
|
|
390
|
+
const argName = inputSubPath[0];
|
|
391
|
+
const wr = resolvePath(schema, session.operation, fieldSchemaPath);
|
|
392
|
+
const argInfo = resolveArgByName(schema, wr, argName);
|
|
393
|
+
const remaining = inputSubPath.slice(1);
|
|
394
|
+
const inputResult = resolveInputPath(schema, argInfo.typeName, remaining);
|
|
395
|
+
return inputResult.inputFields
|
|
396
|
+
.filter((f) => f.typeKind !== "SCALAR" && f.typeKind !== "ENUM")
|
|
397
|
+
.map((f) => `${f.name}/`);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
const schemaPath = queryNavToSchemaPath(navPath);
|
|
401
|
+
if (schemaPath.length === 0) {
|
|
402
|
+
const rootFields = getRootFields(schema, session.operation);
|
|
403
|
+
return rootFields.map((f) => `${f.name}/`);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
const wr = resolvePath(schema, session.operation, schemaPath);
|
|
407
|
+
if (wr.isLeaf) return [];
|
|
408
|
+
const dirs: string[] = [];
|
|
409
|
+
if (wr.args.length > 0) dirs.push("@args/");
|
|
410
|
+
dirs.push(
|
|
411
|
+
...wr.fields
|
|
412
|
+
.filter((f) => f.typeKind !== "SCALAR" && f.typeKind !== "ENUM")
|
|
413
|
+
.map((f) => `${f.name}/`),
|
|
414
|
+
);
|
|
415
|
+
dirs.push(...wr.possibleTypes.map((t) => `[${t}]/`));
|
|
416
|
+
return dirs;
|
|
417
|
+
} catch {
|
|
418
|
+
return [];
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
function parseCdBasePath(session: QuerySession, baseStr: string): string[] {
|
|
423
|
+
if (!baseStr || baseStr === ".") return [...session.navigationPath];
|
|
424
|
+
if (baseStr === "/") return [];
|
|
425
|
+
const absolute = baseStr.startsWith("/");
|
|
426
|
+
const parts = baseStr.split("/").filter((p) => p.length > 0);
|
|
427
|
+
const base = absolute ? [] : [...session.navigationPath];
|
|
428
|
+
for (const part of parts) {
|
|
429
|
+
if (part === ".") continue;
|
|
430
|
+
if (part === "..") {
|
|
431
|
+
base.pop();
|
|
432
|
+
continue;
|
|
433
|
+
}
|
|
434
|
+
base.push(part);
|
|
435
|
+
}
|
|
436
|
+
return base;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
function listFieldsAtPath(session: QuerySession, navPath: string[]): string[] {
|
|
440
|
+
try {
|
|
441
|
+
const schema = getSchema(requireInstanceUrl(session));
|
|
442
|
+
const ctx = getNavigationContext(navPath);
|
|
443
|
+
if (ctx !== "query" || isInArgsContext(navPath)) return [];
|
|
444
|
+
|
|
445
|
+
const schemaPath = queryNavToSchemaPath(navPath);
|
|
446
|
+
if (schemaPath.length === 0) return [];
|
|
447
|
+
|
|
448
|
+
const wr = resolvePath(schema, session.operation, schemaPath);
|
|
449
|
+
if (wr.isLeaf) return [];
|
|
450
|
+
return wr.fields.map((f) => {
|
|
451
|
+
const isDir = f.typeKind !== "SCALAR" && f.typeKind !== "ENUM";
|
|
452
|
+
return isDir ? `${f.name}/` : f.name;
|
|
453
|
+
});
|
|
454
|
+
} catch {
|
|
455
|
+
return [];
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
const PATH_COMMANDS = new Set(["cd", "select", "drop", "set", "var", "unset"]);
|
|
460
|
+
|
|
461
|
+
function makeCompleter(sessionId: string): (line: string) => [string[], string] {
|
|
462
|
+
return function completer(line: string): [string[], string] {
|
|
463
|
+
if (!line.includes(" ")) {
|
|
464
|
+
const matches = TOP_LEVEL_COMMANDS.filter((c) => c.startsWith(line));
|
|
465
|
+
return [matches, line];
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// Extract the command and the last token being typed
|
|
469
|
+
const spaceIdx = line.indexOf(" ");
|
|
470
|
+
const cmd = line.slice(0, spaceIdx).trim();
|
|
471
|
+
if (!PATH_COMMANDS.has(cmd)) return [[], line];
|
|
472
|
+
|
|
473
|
+
// Get the partial text after the last space
|
|
474
|
+
const afterCmd = line.slice(spaceIdx + 1);
|
|
475
|
+
const lastSpaceInArgs = afterCmd.lastIndexOf(" ");
|
|
476
|
+
const partial = lastSpaceInArgs >= 0 ? afterCmd.slice(lastSpaceInArgs + 1) : afterCmd;
|
|
477
|
+
|
|
478
|
+
let session: QuerySession;
|
|
479
|
+
try {
|
|
480
|
+
session = loadSession(sessionId);
|
|
481
|
+
} catch {
|
|
482
|
+
return [[], partial];
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
const lastSlash = partial.lastIndexOf("/");
|
|
486
|
+
const rawBase = lastSlash >= 0 ? partial.slice(0, lastSlash) : "";
|
|
487
|
+
const partialSeg = lastSlash >= 0 ? partial.slice(lastSlash + 1) : partial;
|
|
488
|
+
const baseStr = partial.startsWith("/") && rawBase === "" ? "/" : rawBase;
|
|
489
|
+
|
|
490
|
+
let basePath: string[];
|
|
491
|
+
try {
|
|
492
|
+
basePath = parseCdBasePath(session, baseStr);
|
|
493
|
+
} catch {
|
|
494
|
+
return [[], partialSeg];
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
const candidates: string[] = [];
|
|
498
|
+
const canGoUp = basePath.length > 0 && !baseStr.startsWith("/");
|
|
499
|
+
if (canGoUp) candidates.push("../");
|
|
500
|
+
|
|
501
|
+
// For select, also include leaf fields (not just directories)
|
|
502
|
+
if (cmd === "select") {
|
|
503
|
+
candidates.push(...listFieldsAtPath(session, basePath));
|
|
504
|
+
} else {
|
|
505
|
+
candidates.push(...listDirsAtPath(session, basePath));
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
const lower = partialSeg.toLowerCase();
|
|
509
|
+
const matches = candidates.filter((c) => c.toLowerCase().startsWith(lower));
|
|
510
|
+
|
|
511
|
+
return [matches, partialSeg];
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
export async function runInteractiveSession(sessionId: string): Promise<void> {
|
|
516
|
+
let session = loadSession(sessionId);
|
|
517
|
+
|
|
518
|
+
setInteractiveMode(true);
|
|
519
|
+
|
|
520
|
+
const rl = readline.createInterface({
|
|
521
|
+
input: process.stdin,
|
|
522
|
+
output: process.stdout,
|
|
523
|
+
terminal: true,
|
|
524
|
+
completer: makeCompleter(sessionId),
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
const makePrompt = () => {
|
|
528
|
+
const path = formatPath(session.navigationPath);
|
|
529
|
+
return `> graphiti [${session.id}] ${path} $ `;
|
|
530
|
+
};
|
|
531
|
+
|
|
532
|
+
console.log(`Interactive session ${session.id} (org: ${session.orgAlias})`);
|
|
533
|
+
console.log('Type a command or "help" for a list. Press Ctrl-C or type "exit" to quit.');
|
|
534
|
+
|
|
535
|
+
const processLine = async (line: string): Promise<boolean> => {
|
|
536
|
+
const trimmed = line.trim();
|
|
537
|
+
if (!trimmed) return true;
|
|
538
|
+
|
|
539
|
+
if (trimmed === "exit" || trimmed === "quit") {
|
|
540
|
+
return false;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
const tokens = tokenize(trimmed);
|
|
544
|
+
if (tokens.length === 0) return true;
|
|
545
|
+
|
|
546
|
+
const [cmd, ...rest] = tokens;
|
|
547
|
+
|
|
548
|
+
const opts: {
|
|
549
|
+
as?: string;
|
|
550
|
+
long?: boolean;
|
|
551
|
+
all?: boolean;
|
|
552
|
+
search?: string;
|
|
553
|
+
regex?: string;
|
|
554
|
+
dataCloud?: boolean;
|
|
555
|
+
} = {};
|
|
556
|
+
const cleanRest: string[] = [];
|
|
557
|
+
const expanded: string[] = [];
|
|
558
|
+
for (const token of rest) {
|
|
559
|
+
if (/^-[a-zA-Z]{2,}$/.test(token)) {
|
|
560
|
+
for (const ch of token.slice(1)) expanded.push(`-${ch}`);
|
|
561
|
+
} else {
|
|
562
|
+
expanded.push(token);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
for (let i = 0; i < expanded.length; i++) {
|
|
566
|
+
if (expanded[i] === "--as" && expanded[i + 1]) {
|
|
567
|
+
opts.as = expanded[++i];
|
|
568
|
+
} else if (expanded[i] === "-l" || expanded[i] === "--long") {
|
|
569
|
+
opts.long = true;
|
|
570
|
+
} else if (expanded[i] === "-a" || expanded[i] === "--all") {
|
|
571
|
+
opts.all = true;
|
|
572
|
+
} else if (expanded[i] === "--search" && expanded[i + 1]) {
|
|
573
|
+
opts.search = expanded[++i];
|
|
574
|
+
} else if (expanded[i] === "--regex" && expanded[i + 1]) {
|
|
575
|
+
opts.regex = expanded[++i];
|
|
576
|
+
} else if (expanded[i] === "--data-cloud") {
|
|
577
|
+
opts.dataCloud = true;
|
|
578
|
+
} else {
|
|
579
|
+
cleanRest.push(expanded[i]);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
try {
|
|
584
|
+
if (cmd === "select" && cleanRest.length === 0) {
|
|
585
|
+
await guidedSelect(sessionId, rl);
|
|
586
|
+
} else if (cmd === "cd" && cleanRest.length === 0) {
|
|
587
|
+
await guidedCd(sessionId, rl);
|
|
588
|
+
} else if (cmd === "set" && cleanRest.length === 0) {
|
|
589
|
+
await guidedAssign(sessionId, rl);
|
|
590
|
+
} else if (cmd === "run" && cleanRest.length === 0) {
|
|
591
|
+
await guidedExecute(sessionId, rl);
|
|
592
|
+
} else {
|
|
593
|
+
await dispatchQueryCommand(sessionId, cmd, cleanRest, opts);
|
|
594
|
+
}
|
|
595
|
+
} catch (err) {
|
|
596
|
+
if (err instanceof CommandError) {
|
|
597
|
+
console.error(`Error: ${err.message}`);
|
|
598
|
+
} else {
|
|
599
|
+
console.error(`Unexpected error: ${err instanceof Error ? err.message : String(err)}`);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
try {
|
|
604
|
+
session = loadSession(sessionId);
|
|
605
|
+
} catch {
|
|
606
|
+
// Session may have been deleted.
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
return true;
|
|
610
|
+
};
|
|
611
|
+
|
|
612
|
+
await new Promise<void>((resolve) => {
|
|
613
|
+
const prompt = () =>
|
|
614
|
+
rl.question(makePrompt(), async (line) => {
|
|
615
|
+
const continueLoop = await processLine(line);
|
|
616
|
+
if (continueLoop) {
|
|
617
|
+
prompt();
|
|
618
|
+
} else {
|
|
619
|
+
console.log("Goodbye.");
|
|
620
|
+
rl.close();
|
|
621
|
+
resolve();
|
|
622
|
+
}
|
|
623
|
+
});
|
|
624
|
+
|
|
625
|
+
rl.on("close", () => {
|
|
626
|
+
console.log("");
|
|
627
|
+
resolve();
|
|
628
|
+
});
|
|
629
|
+
|
|
630
|
+
prompt();
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
setInteractiveMode(false);
|
|
634
|
+
}
|