@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,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026, Salesforce, Inc.,
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* For full license text, see the LICENSE.txt file
|
|
5
|
+
*/
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import { buildRaw } from "../../intent/build-raw.js";
|
|
8
|
+
const inputSchema = {
|
|
9
|
+
org: z.string().describe("Org alias resolved via local Salesforce CLI auth (~/.sf, ~/.sfdx)."),
|
|
10
|
+
commands: z
|
|
11
|
+
.array(z.string())
|
|
12
|
+
.min(1)
|
|
13
|
+
.describe("CLI-style commands applied in order to a transient session. Supported verbs:\n" +
|
|
14
|
+
" select <path[:alias]> e.g. select uiapi/query/Case/edges/node/Subject/value\n" +
|
|
15
|
+
" set [<path>] <key>=<value> e.g. set uiapi/query/Case first=10 | set uiapi/query/Case where.Status=New\n" +
|
|
16
|
+
" var $name <path> [default] e.g. var $id uiapi/query/Case/@args/where/Id/eq (type inferred from path)\n" +
|
|
17
|
+
'Each command is tokenized on spaces and a value MUST NOT contain a space — quoting does not help once a token has started (key=\'a b\' still splits). A filter value that contains a space (e.g. "New York", "In Progress") cannot be expressed via set in v1; use sf_gql_list with a JSON filter, or pass the value through a variable bound with var.\n' +
|
|
18
|
+
"Fails fast: a bad command aborts the whole call. Other CLI verbs (cd, drop, alias, optional, unset) are NOT supported in v1."),
|
|
19
|
+
operation: z
|
|
20
|
+
.enum(["query", "mutation", "aggregate"])
|
|
21
|
+
.optional()
|
|
22
|
+
.describe('Operation root. "query" (default) → uiapi.query; "mutation" → mutation root; "aggregate" → uiapi.aggregate.'),
|
|
23
|
+
typeName: z
|
|
24
|
+
.string()
|
|
25
|
+
.optional()
|
|
26
|
+
.describe("Codegen type prefix / operation name override. Defaults to Raw<Operation>."),
|
|
27
|
+
};
|
|
28
|
+
export function registerSfGqlRawTool(server, opts = {}) {
|
|
29
|
+
server.registerTool("sf_gql_raw", {
|
|
30
|
+
description: "Low-level escape hatch: build an arbitrary UIAPI query, mutation, or aggregate from CLI-style commands (select/set/var) when the typed tools don't model the case (cross-union selections, custom mutations). Returns rendered GraphQL, declared variables, generated TypeScript types, and validation warnings. Fails fast on a bad command.",
|
|
31
|
+
inputSchema,
|
|
32
|
+
}, async (args) => {
|
|
33
|
+
const output = await buildRaw(args, opts.primeDeps);
|
|
34
|
+
return {
|
|
35
|
+
content: [{ type: "text", text: JSON.stringify(output) }],
|
|
36
|
+
};
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=sf-gql-raw.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sf-gql-raw.js","sourceRoot":"","sources":["../../../src/mcp/tools/sf-gql-raw.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAOrD,MAAM,WAAW,GAAG;IACnB,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,oEAAoE,CAAC;IAC9F,QAAQ,EAAE,CAAC;SACT,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;SACjB,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,CACR,gFAAgF;QAC/E,sFAAsF;QACtF,6GAA6G;QAC7G,0GAA0G;QAC1G,2VAA2V;QAC3V,8HAA8H,CAC/H;IACF,SAAS,EAAE,CAAC;SACV,IAAI,CAAC,CAAC,OAAO,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC;SACxC,QAAQ,EAAE;SACV,QAAQ,CACR,6GAA6G,CAC7G;IACF,QAAQ,EAAE,CAAC;SACT,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CAAC,4EAA4E,CAAC;CACxF,CAAC;AAEF,MAAM,UAAU,oBAAoB,CAAC,MAAiB,EAAE,OAA4B,EAAE;IACrF,MAAM,CAAC,YAAY,CAClB,YAAY,EACZ;QACC,WAAW,EACV,+UAA+U;QAChV,WAAW;KACX,EACD,KAAK,EAAE,IAAI,EAAE,EAAE;QACd,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QACpD,OAAO;YACN,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;SACzD,CAAC;IACH,CAAC,CACD,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026, Salesforce, Inc.,
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* For full license text, see the LICENSE.txt file
|
|
5
|
+
*/
|
|
6
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
7
|
+
import { type PrimeDeps } from "../../lib/prime-schema.js";
|
|
8
|
+
export interface SfGqlUpdateToolOptions {
|
|
9
|
+
primeDeps?: PrimeDeps;
|
|
10
|
+
}
|
|
11
|
+
export declare function registerSfGqlUpdateTool(server: McpServer, opts?: SfGqlUpdateToolOptions): void;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026, Salesforce, Inc.,
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* For full license text, see the LICENSE.txt file
|
|
5
|
+
*/
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import { buildUpdate } from "../../intent/build-update.js";
|
|
8
|
+
const inputSchema = {
|
|
9
|
+
org: z.string().describe("Org alias resolved via local Salesforce CLI auth (~/.sf, ~/.sfdx)."),
|
|
10
|
+
object: z.string().describe('SObject API name, e.g. "Account", "Order".'),
|
|
11
|
+
returnFields: z
|
|
12
|
+
.array(z.string())
|
|
13
|
+
.optional()
|
|
14
|
+
.describe('Scalar field API names to read back on the updated Record. Defaults to ["Id"]. Dot-paths like Owner.Name are not supported in mutation results; passing them produces a warning and the field is skipped.'),
|
|
15
|
+
inputVariable: z
|
|
16
|
+
.string()
|
|
17
|
+
.optional()
|
|
18
|
+
.describe('Variable name (without "$") for the update input. Defaults to "input". Declared as $<name>: <Object>UpdateInput!.'),
|
|
19
|
+
operationName: z
|
|
20
|
+
.string()
|
|
21
|
+
.optional()
|
|
22
|
+
.describe("Override the GraphQL operation name. Defaults to Update<Object>."),
|
|
23
|
+
};
|
|
24
|
+
export function registerSfGqlUpdateTool(server, opts = {}) {
|
|
25
|
+
server.registerTool("sf_gql_update", {
|
|
26
|
+
description: "Build a UIAPI update mutation against a Salesforce org. Declares a typed input variable ($input: <Object>UpdateInput!) and selects return fields on the updated record. Returns rendered GraphQL, declared variables, generated TypeScript types, and validation warnings.",
|
|
27
|
+
inputSchema,
|
|
28
|
+
}, async (args) => {
|
|
29
|
+
const output = await buildUpdate(args, opts.primeDeps);
|
|
30
|
+
return {
|
|
31
|
+
content: [{ type: "text", text: JSON.stringify(output) }],
|
|
32
|
+
};
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=sf-gql-update.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sf-gql-update.js","sourceRoot":"","sources":["../../../src/mcp/tools/sf-gql-update.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAO3D,MAAM,WAAW,GAAG;IACnB,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,oEAAoE,CAAC;IAC9F,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,4CAA4C,CAAC;IACzE,YAAY,EAAE,CAAC;SACb,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;SACjB,QAAQ,EAAE;SACV,QAAQ,CACR,2MAA2M,CAC3M;IACF,aAAa,EAAE,CAAC;SACd,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CACR,mHAAmH,CACnH;IACF,aAAa,EAAE,CAAC;SACd,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CAAC,kEAAkE,CAAC;CAC9E,CAAC;AAEF,MAAM,UAAU,uBAAuB,CACtC,MAAiB,EACjB,OAA+B,EAAE;IAEjC,MAAM,CAAC,YAAY,CAClB,eAAe,EACf;QACC,WAAW,EACV,4QAA4Q;QAC7Q,WAAW;KACX,EACD,KAAK,EAAE,IAAI,EAAE,EAAE;QACd,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QACvD,OAAO;YACN,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;SACzD,CAAC;IACH,CAAC,CACD,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026, Salesforce, Inc.,
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* For full license text, see the LICENSE.txt file
|
|
5
|
+
*/
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
/**
|
|
8
|
+
* Zod string validator that enforces the GraphQL Name production. Returned
|
|
9
|
+
* schema is `.describe()`-tagged with the supplied description so it appears
|
|
10
|
+
* in the MCP tool's advertised JSON Schema.
|
|
11
|
+
*/
|
|
12
|
+
export declare const graphqlName: (description: string) => z.ZodString;
|
|
13
|
+
export declare const orgAlias: (description: string) => z.ZodString;
|
|
14
|
+
/**
|
|
15
|
+
* `<Object>_OrderBy` shape. UIAPI accepts a singleton object; tools may
|
|
16
|
+
* additionally accept arrays as a compat shim depending on their advertised
|
|
17
|
+
* schema policy (see `sf-gql-list.ts` vs `sf-gql-detail.ts`).
|
|
18
|
+
*/
|
|
19
|
+
export declare const orderByObject: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
20
|
+
/**
|
|
21
|
+
* Build a `childRelationships[]` element schema. The `orderBy` schema is a
|
|
22
|
+
* parameter because tools differ on whether they advertise the array shape
|
|
23
|
+
* (`sf_gql_list`: union; `sf_gql_detail`: singleton with array-collapse
|
|
24
|
+
* preprocess). Everything else — `relationshipName`, `fields`, `first`,
|
|
25
|
+
* `filter` — is identical across callers.
|
|
26
|
+
*
|
|
27
|
+
* `relationshipName` flows into a rendered field-path position in the
|
|
28
|
+
* resulting query, so it carries the same GraphQL Name constraint as
|
|
29
|
+
* top-level `object` — same failure mode if it's invalid.
|
|
30
|
+
*/
|
|
31
|
+
export declare const childRelationshipSchema: (orderBy: z.ZodTypeAny) => z.ZodObject<{
|
|
32
|
+
relationshipName: z.ZodString;
|
|
33
|
+
fields: z.ZodArray<z.ZodString, "many">;
|
|
34
|
+
first: z.ZodOptional<z.ZodNumber>;
|
|
35
|
+
filter: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
36
|
+
orderBy: z.ZodOptional<z.ZodTypeAny>;
|
|
37
|
+
}, "strip", z.ZodTypeAny, {
|
|
38
|
+
fields: string[];
|
|
39
|
+
relationshipName: string;
|
|
40
|
+
filter?: Record<string, unknown> | undefined;
|
|
41
|
+
orderBy?: any;
|
|
42
|
+
first?: number | undefined;
|
|
43
|
+
}, {
|
|
44
|
+
fields: string[];
|
|
45
|
+
relationshipName: string;
|
|
46
|
+
filter?: Record<string, unknown> | undefined;
|
|
47
|
+
orderBy?: any;
|
|
48
|
+
first?: number | undefined;
|
|
49
|
+
}>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026, Salesforce, Inc.,
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* For full license text, see the LICENSE.txt file
|
|
5
|
+
*/
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import { GRAPHQL_NAME_RE } from "../../../lib/graphql-name.js";
|
|
8
|
+
/**
|
|
9
|
+
* Zod string validator that enforces the GraphQL Name production. Returned
|
|
10
|
+
* schema is `.describe()`-tagged with the supplied description so it appears
|
|
11
|
+
* in the MCP tool's advertised JSON Schema.
|
|
12
|
+
*/
|
|
13
|
+
export const graphqlName = (description) => z.string().regex(GRAPHQL_NAME_RE, "must be a valid GraphQL Name").describe(description);
|
|
14
|
+
/**
|
|
15
|
+
* Org alias / username charset + length, matching `auth.ts`'s
|
|
16
|
+
* `assertValidOrgAlias`. Defense-in-depth at the MCP boundary so the
|
|
17
|
+
* advertised JSON Schema teaches the LLM the constraint upfront, while
|
|
18
|
+
* `assertValidOrgAlias` still gates the shell-out path.
|
|
19
|
+
*/
|
|
20
|
+
const ORG_ALIAS_RE = /^[A-Za-z0-9_][A-Za-z0-9_.+@-]{0,252}$/;
|
|
21
|
+
export const orgAlias = (description) => z.string().regex(ORG_ALIAS_RE, "must be a valid org alias or username").describe(description);
|
|
22
|
+
/**
|
|
23
|
+
* `<Object>_OrderBy` shape. UIAPI accepts a singleton object; tools may
|
|
24
|
+
* additionally accept arrays as a compat shim depending on their advertised
|
|
25
|
+
* schema policy (see `sf-gql-list.ts` vs `sf-gql-detail.ts`).
|
|
26
|
+
*/
|
|
27
|
+
export const orderByObject = z.record(z.unknown());
|
|
28
|
+
/**
|
|
29
|
+
* Build a `childRelationships[]` element schema. The `orderBy` schema is a
|
|
30
|
+
* parameter because tools differ on whether they advertise the array shape
|
|
31
|
+
* (`sf_gql_list`: union; `sf_gql_detail`: singleton with array-collapse
|
|
32
|
+
* preprocess). Everything else — `relationshipName`, `fields`, `first`,
|
|
33
|
+
* `filter` — is identical across callers.
|
|
34
|
+
*
|
|
35
|
+
* `relationshipName` flows into a rendered field-path position in the
|
|
36
|
+
* resulting query, so it carries the same GraphQL Name constraint as
|
|
37
|
+
* top-level `object` — same failure mode if it's invalid.
|
|
38
|
+
*/
|
|
39
|
+
export const childRelationshipSchema = (orderBy) => z.object({
|
|
40
|
+
relationshipName: graphqlName('Child relationship API name, e.g. "Contacts", "Opportunities". Must be a valid GraphQL Name.'),
|
|
41
|
+
fields: z.array(z.string()),
|
|
42
|
+
first: z.number().int().positive().optional(),
|
|
43
|
+
filter: z.record(z.unknown()).optional(),
|
|
44
|
+
orderBy: orderBy.optional(),
|
|
45
|
+
});
|
|
46
|
+
//# sourceMappingURL=zod-schemas.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zod-schemas.js","sourceRoot":"","sources":["../../../../src/mcp/tools/shared/zod-schemas.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAE/D;;;;GAIG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,WAAmB,EAAe,EAAE,CAC/D,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,eAAe,EAAE,8BAA8B,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AAEzF;;;;;GAKG;AACH,MAAM,YAAY,GAAG,uCAAuC,CAAC;AAC7D,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,WAAmB,EAAe,EAAE,CAC5D,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,YAAY,EAAE,uCAAuC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AAE/F;;;;GAIG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;AAEnD;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,OAAqB,EAAE,EAAE,CAChE,CAAC,CAAC,MAAM,CAAC;IACR,gBAAgB,EAAE,WAAW,CAC5B,8FAA8F,CAC9F;IACD,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAC3B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC7C,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,EAAE;IACxC,OAAO,EAAE,OAAO,CAAC,QAAQ,EAAE;CAC3B,CAAC,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@salesforce/graphiti",
|
|
3
|
+
"version": "10.10.2",
|
|
4
|
+
"description": "Progressive GraphQL query builder CLI for Salesforce orgs",
|
|
5
|
+
"license": "SEE LICENSE IN LICENSE.txt",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"graphiti": "./dist/cli.js",
|
|
9
|
+
"graphiti-mcp": "./dist/mcp/stdio.js"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"dev": "tsx src/cli.ts",
|
|
14
|
+
"start": "node dist/cli.js",
|
|
15
|
+
"test": "vitest run",
|
|
16
|
+
"test:watch": "vitest",
|
|
17
|
+
"test:coverage": "vitest run --coverage"
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
21
|
+
"@salesforce/core": "^8.23.4",
|
|
22
|
+
"commander": "^13.1.0",
|
|
23
|
+
"graphql": "^16.10.0",
|
|
24
|
+
"zod": "^3.25.0"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@types/node": "^22.13.0",
|
|
28
|
+
"@vitest/coverage-v8": "^4.0.6",
|
|
29
|
+
"tsx": "^4.19.0",
|
|
30
|
+
"typescript": "^5.7.0",
|
|
31
|
+
"vitest": "^4.0.6"
|
|
32
|
+
},
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public"
|
|
35
|
+
}
|
|
36
|
+
}
|
package/ralph-loop.sh
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Ralph loop: repeatedly hand a TASK.md file to Claude and let it iterate.
|
|
5
|
+
#
|
|
6
|
+
# All task-specific instructions (phases, rules, priorities, tools, etc.)
|
|
7
|
+
# belong in the task file itself — this script stays generic and just drives
|
|
8
|
+
# the loop.
|
|
9
|
+
#
|
|
10
|
+
# Usage:
|
|
11
|
+
# ./ralph-loop.sh [TASK_FILE] [ITERATIONS]
|
|
12
|
+
#
|
|
13
|
+
# Environment variables:
|
|
14
|
+
# TASK_FILE Path to the task markdown file (default: TASK.md)
|
|
15
|
+
# ITERATIONS Number of iterations to run (default: 100)
|
|
16
|
+
# REFLECTION_FILE Path to the reflection file the agent reads/writes
|
|
17
|
+
# between runs (default: REFLECTION.md)
|
|
18
|
+
# MODEL Claude model to use (default: opus)
|
|
19
|
+
# WORK_DIR Directory to run the loop in (default: script dir)
|
|
20
|
+
# PRE_CMD Optional command to run once before the loop starts
|
|
21
|
+
# ITER_CMD Optional command to run after each iteration
|
|
22
|
+
# LOG_DIR Where to write per-iteration logs
|
|
23
|
+
# (default: $WORK_DIR/ralph-logs)
|
|
24
|
+
# STOP_FILE If the agent creates this file, the loop exits early
|
|
25
|
+
# (default: DONE)
|
|
26
|
+
|
|
27
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
28
|
+
|
|
29
|
+
TASK_FILE="${1:-${TASK_FILE:-TASK.md}}"
|
|
30
|
+
TOTAL="${2:-${ITERATIONS:-100}}"
|
|
31
|
+
REFLECTION_FILE="${REFLECTION_FILE:-REFLECTION.md}"
|
|
32
|
+
MODEL="${MODEL:-opus}"
|
|
33
|
+
WORK_DIR="${WORK_DIR:-$SCRIPT_DIR}"
|
|
34
|
+
LOG_DIR="${LOG_DIR:-$WORK_DIR/ralph-logs}"
|
|
35
|
+
STOP_FILE="${STOP_FILE:-DONE}"
|
|
36
|
+
|
|
37
|
+
cd "$WORK_DIR"
|
|
38
|
+
|
|
39
|
+
if [ ! -f "$TASK_FILE" ]; then
|
|
40
|
+
echo "ERROR: task file not found: $TASK_FILE" >&2
|
|
41
|
+
echo "Create a $TASK_FILE describing what you want the agent to do," >&2
|
|
42
|
+
echo "or pass a path: ./ralph-loop.sh path/to/task.md" >&2
|
|
43
|
+
exit 1
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
mkdir -p "$LOG_DIR"
|
|
47
|
+
|
|
48
|
+
echo "Ralph loop configuration:"
|
|
49
|
+
echo " Task file: $TASK_FILE"
|
|
50
|
+
echo " Reflection file: $REFLECTION_FILE"
|
|
51
|
+
echo " Iterations: $TOTAL"
|
|
52
|
+
echo " Model: $MODEL"
|
|
53
|
+
echo " Work dir: $WORK_DIR"
|
|
54
|
+
echo " Log dir: $LOG_DIR"
|
|
55
|
+
echo ""
|
|
56
|
+
|
|
57
|
+
if [ -n "${PRE_CMD:-}" ]; then
|
|
58
|
+
echo "Running PRE_CMD: $PRE_CMD"
|
|
59
|
+
bash -c "$PRE_CMD"
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
for i in $(seq 1 "$TOTAL"); do
|
|
63
|
+
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
|
|
64
|
+
LOG_FILE="$LOG_DIR/run-${i}-${TIMESTAMP}.log"
|
|
65
|
+
|
|
66
|
+
echo ""
|
|
67
|
+
echo "========================================="
|
|
68
|
+
echo " RALPH LOOP — iteration $i / $TOTAL"
|
|
69
|
+
echo " $(date)"
|
|
70
|
+
echo " Log: $LOG_FILE"
|
|
71
|
+
echo "========================================="
|
|
72
|
+
echo ""
|
|
73
|
+
|
|
74
|
+
PROMPT="Read the file $TASK_FILE in this repository and execute it end-to-end. \
|
|
75
|
+
The task file is the source of truth — follow its phases, rules, and priorities exactly. \
|
|
76
|
+
This is iteration $i of $TOTAL. \
|
|
77
|
+
If $REFLECTION_FILE already exists from a previous iteration, read it first and factor in \
|
|
78
|
+
prior learnings — do not repeat the same mistakes, and build on what was already done. \
|
|
79
|
+
Update $REFLECTION_FILE at the end of this iteration with what you learned and what should \
|
|
80
|
+
change next time. \
|
|
81
|
+
Do not ask the user for input at any point — make reasonable decisions and keep going. \
|
|
82
|
+
When all success criteria in the task file are met, create a file named '$STOP_FILE' \
|
|
83
|
+
(just 'touch $STOP_FILE') to signal the loop should stop. Only create it when you are \
|
|
84
|
+
confident the task is fully complete. \
|
|
85
|
+
When you finish, print a concise summary of what you accomplished in this iteration."
|
|
86
|
+
|
|
87
|
+
claude \
|
|
88
|
+
--print \
|
|
89
|
+
--dangerously-skip-permissions \
|
|
90
|
+
--model "$MODEL" \
|
|
91
|
+
--verbose \
|
|
92
|
+
"$PROMPT" \
|
|
93
|
+
2>&1 | tee "$LOG_FILE"
|
|
94
|
+
|
|
95
|
+
EXIT_CODE=${PIPESTATUS[0]}
|
|
96
|
+
|
|
97
|
+
echo ""
|
|
98
|
+
echo "Iteration $i finished with exit code $EXIT_CODE at $(date)"
|
|
99
|
+
echo "---"
|
|
100
|
+
|
|
101
|
+
if [ "$EXIT_CODE" -ne 0 ]; then
|
|
102
|
+
echo "WARNING: Iteration $i exited non-zero ($EXIT_CODE). Continuing..."
|
|
103
|
+
fi
|
|
104
|
+
|
|
105
|
+
if [ -n "${ITER_CMD:-}" ]; then
|
|
106
|
+
echo "Running ITER_CMD: $ITER_CMD"
|
|
107
|
+
bash -c "$ITER_CMD" || true
|
|
108
|
+
fi
|
|
109
|
+
|
|
110
|
+
if [ -f "$WORK_DIR/$STOP_FILE" ]; then
|
|
111
|
+
echo ""
|
|
112
|
+
echo "Stop file found ($STOP_FILE) — task complete, exiting early after iteration $i."
|
|
113
|
+
break
|
|
114
|
+
fi
|
|
115
|
+
done
|
|
116
|
+
|
|
117
|
+
echo ""
|
|
118
|
+
echo "========================================="
|
|
119
|
+
echo " RALPH LOOP COMPLETE — $TOTAL iterations"
|
|
120
|
+
echo "========================================="
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Smoke test: orderBy support for sf_gql_aggregate
|
|
3
|
+
#
|
|
4
|
+
# Tests a105, a114, a115, a119 from the aggregates assignment.
|
|
5
|
+
# Exits 0 only if all 4 pass. Writes results to stdout and
|
|
6
|
+
# SMOKE-RESULTS-orderby.txt for the ralph loop reflection.
|
|
7
|
+
#
|
|
8
|
+
# Usage: cd packages/graphiti && bash scripts/smoke-orderby.sh
|
|
9
|
+
#
|
|
10
|
+
# Prerequisites:
|
|
11
|
+
# - pnpm build (clean, dist/ up to date)
|
|
12
|
+
# - ebikes org authenticated via sf CLI (only needed for live runs;
|
|
13
|
+
# the tool validates the schema cache, not live data)
|
|
14
|
+
|
|
15
|
+
set -uo pipefail
|
|
16
|
+
|
|
17
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
18
|
+
WORK_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
19
|
+
cd "$WORK_DIR"
|
|
20
|
+
|
|
21
|
+
RESULTS_FILE="$WORK_DIR/SMOKE-RESULTS-orderby.txt"
|
|
22
|
+
PASS_COUNT=0
|
|
23
|
+
FAIL_COUNT=0
|
|
24
|
+
DETAILS=""
|
|
25
|
+
|
|
26
|
+
call_tool() {
|
|
27
|
+
local args="$1"
|
|
28
|
+
printf '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"0.1"}}}\n{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"sf_gql_aggregate","arguments":%s}}\n' "$args" \
|
|
29
|
+
| FORCE_COLOR=0 NO_COLOR=1 node dist/mcp/stdio.js 2>/dev/null \
|
|
30
|
+
| sed -n '2p'
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
extract_query() {
|
|
34
|
+
python3 -c "
|
|
35
|
+
import sys, json
|
|
36
|
+
line = sys.stdin.read().strip()
|
|
37
|
+
data = json.loads(line)
|
|
38
|
+
content = data.get('result',{}).get('content',[{}])[0].get('text','')
|
|
39
|
+
is_error = data.get('result',{}).get('isError', False)
|
|
40
|
+
if is_error:
|
|
41
|
+
print('ERROR: ' + content)
|
|
42
|
+
else:
|
|
43
|
+
parsed = json.loads(content)
|
|
44
|
+
print(parsed.get('query',''))
|
|
45
|
+
"
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
normalize() {
|
|
49
|
+
# Strip whitespace differences for comparison
|
|
50
|
+
python3 -c "
|
|
51
|
+
import sys, re
|
|
52
|
+
q = sys.stdin.read().strip()
|
|
53
|
+
# Collapse all whitespace to single spaces
|
|
54
|
+
q = re.sub(r'\s+', ' ', q)
|
|
55
|
+
print(q)
|
|
56
|
+
"
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
check_orderby() {
|
|
60
|
+
local id="$1"
|
|
61
|
+
local input="$2"
|
|
62
|
+
local expected_query="$3"
|
|
63
|
+
local description="$4"
|
|
64
|
+
|
|
65
|
+
local raw_result
|
|
66
|
+
raw_result=$(call_tool "$input")
|
|
67
|
+
|
|
68
|
+
local query
|
|
69
|
+
query=$(echo "$raw_result" | extract_query)
|
|
70
|
+
|
|
71
|
+
local status="FAIL"
|
|
72
|
+
local note=""
|
|
73
|
+
|
|
74
|
+
if echo "$query" | grep -q "^ERROR:"; then
|
|
75
|
+
note="Tool returned error: $query"
|
|
76
|
+
else
|
|
77
|
+
# Normalize both for comparison
|
|
78
|
+
local actual_norm
|
|
79
|
+
actual_norm=$(echo "$query" | normalize)
|
|
80
|
+
local expected_norm
|
|
81
|
+
expected_norm=$(echo "$expected_query" | normalize)
|
|
82
|
+
|
|
83
|
+
if [ "$actual_norm" = "$expected_norm" ]; then
|
|
84
|
+
status="PASS"
|
|
85
|
+
note="Query matches expected output exactly"
|
|
86
|
+
else
|
|
87
|
+
# Check key structural elements even if not exact match
|
|
88
|
+
local has_orderby=false
|
|
89
|
+
local has_function=false
|
|
90
|
+
local has_order=false
|
|
91
|
+
|
|
92
|
+
if echo "$query" | grep -q "orderBy"; then
|
|
93
|
+
has_orderby=true
|
|
94
|
+
fi
|
|
95
|
+
if echo "$query" | grep -q "function:"; then
|
|
96
|
+
has_function=true
|
|
97
|
+
fi
|
|
98
|
+
if echo "$query" | grep -qi "order:.*\(ASC\|DESC\)"; then
|
|
99
|
+
has_order=true
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
if [ "$has_orderby" = true ] && [ "$has_function" = true ] && [ "$has_order" = true ]; then
|
|
103
|
+
# Has the right shape but doesn't match exactly
|
|
104
|
+
note="Has orderBy with function and order direction but differs from expected.
|
|
105
|
+
Expected: $expected_norm
|
|
106
|
+
Actual: $actual_norm"
|
|
107
|
+
elif [ "$has_orderby" = false ]; then
|
|
108
|
+
note="Missing orderBy clause entirely. Query: $(echo "$query" | head -5)"
|
|
109
|
+
else
|
|
110
|
+
note="orderBy present but missing function or order direction.
|
|
111
|
+
Expected: $expected_norm
|
|
112
|
+
Actual: $actual_norm"
|
|
113
|
+
fi
|
|
114
|
+
fi
|
|
115
|
+
fi
|
|
116
|
+
|
|
117
|
+
if [ "$status" = "PASS" ]; then
|
|
118
|
+
PASS_COUNT=$((PASS_COUNT + 1))
|
|
119
|
+
else
|
|
120
|
+
FAIL_COUNT=$((FAIL_COUNT + 1))
|
|
121
|
+
fi
|
|
122
|
+
|
|
123
|
+
local entry="### $id — $description
|
|
124
|
+
**Status:** $status
|
|
125
|
+
**Input:** $input
|
|
126
|
+
**Note:** $note
|
|
127
|
+
**Query output:**
|
|
128
|
+
\`\`\`graphql
|
|
129
|
+
$query
|
|
130
|
+
\`\`\`
|
|
131
|
+
**Expected:**
|
|
132
|
+
\`\`\`graphql
|
|
133
|
+
$expected_query
|
|
134
|
+
\`\`\`
|
|
135
|
+
"
|
|
136
|
+
DETAILS="${DETAILS}${entry}
|
|
137
|
+
"
|
|
138
|
+
echo "$status: $id — $note"
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
echo "=== OrderBy Smoke Tests ==="
|
|
142
|
+
echo ""
|
|
143
|
+
|
|
144
|
+
# a105: count Leads, groupBy CreatedDate CALENDAR_YEAR, orderBy Description count DESC
|
|
145
|
+
check_orderby \
|
|
146
|
+
"a105_leads_by_created_year_ordered" \
|
|
147
|
+
'{"org":"ebikes","object":"Lead","aggregations":[{"function":"count"}],"groupBy":[{"field":"CreatedDate","function":"CALENDAR_YEAR"}],"orderBy":{"Description":{"function":"COUNT","order":"DESC"}},"operationName":"LeadsByCreatedYearOrdered"}' \
|
|
148
|
+
'query LeadsByCreatedYearOrdered($after: String) { uiapi { aggregate { Lead(groupBy: { CreatedDate: { function: CALENDAR_YEAR } }, orderBy: { Description: { function: COUNT, order: DESC } }, after: $after) { edges { node { aggregate { CreatedDate { value } countId: Id { count { value } } } } } pageInfo { hasNextPage endCursor } } } } }' \
|
|
149
|
+
"count Leads groupBy CreatedDate CALENDAR_YEAR, orderBy Description count DESC"
|
|
150
|
+
|
|
151
|
+
# a114: count Opportunities, groupBy AccountId, orderBy Description count DESC
|
|
152
|
+
check_orderby \
|
|
153
|
+
"a114_opps_by_account_ordered_by_count" \
|
|
154
|
+
'{"org":"ebikes","object":"Opportunity","aggregations":[{"function":"count"}],"groupBy":["AccountId"],"orderBy":{"Description":{"function":"COUNT","order":"DESC"}},"operationName":"OppsByAccountOrdered"}' \
|
|
155
|
+
'query OppsByAccountOrdered($after: String) { uiapi { aggregate { Opportunity(groupBy: { AccountId: { group: true } }, orderBy: { Description: { function: COUNT, order: DESC } }, after: $after) { edges { node { aggregate { AccountId { value } countId: Id { count { value } } } } } pageInfo { hasNextPage endCursor } } } } }' \
|
|
156
|
+
"count Opportunities groupBy AccountId, orderBy Description count DESC"
|
|
157
|
+
|
|
158
|
+
# a115: count Cases, groupBy Priority, orderBy Description count ASC
|
|
159
|
+
check_orderby \
|
|
160
|
+
"a115_cases_by_priority_ordered_by_count" \
|
|
161
|
+
'{"org":"ebikes","object":"Case","aggregations":[{"function":"count"}],"groupBy":["Priority"],"orderBy":{"Description":{"function":"COUNT","order":"ASC"}},"operationName":"CasesByPriorityOrdered"}' \
|
|
162
|
+
'query CasesByPriorityOrdered($after: String) { uiapi { aggregate { Case(groupBy: { Priority: { group: true } }, orderBy: { Description: { function: COUNT, order: ASC } }, after: $after) { edges { node { aggregate { Priority { value } countId: Id { count { value } } } } } pageInfo { hasNextPage endCursor } } } } }' \
|
|
163
|
+
"count Cases groupBy Priority, orderBy Description count ASC"
|
|
164
|
+
|
|
165
|
+
# a119: count Accounts, groupBy Industry + OwnerId, orderBy Description count DESC
|
|
166
|
+
check_orderby \
|
|
167
|
+
"a119_accounts_by_industry_and_owner_ordered" \
|
|
168
|
+
'{"org":"ebikes","object":"Account","aggregations":[{"function":"count"}],"groupBy":["Industry","OwnerId"],"orderBy":{"Description":{"function":"COUNT","order":"DESC"}},"operationName":"AccountsByIndustryOwnerOrdered"}' \
|
|
169
|
+
'query AccountsByIndustryOwnerOrdered($after: String) { uiapi { aggregate { Account(groupBy: { Industry: { group: true }, OwnerId: { group: true } }, orderBy: { Description: { function: COUNT, order: DESC } }, after: $after) { edges { node { aggregate { Industry { value } OwnerId { value } countId: Id { count { value } } } } } pageInfo { hasNextPage endCursor } } } } }' \
|
|
170
|
+
"count Accounts groupBy Industry + OwnerId, orderBy Description count DESC"
|
|
171
|
+
|
|
172
|
+
echo ""
|
|
173
|
+
echo "=== Summary: $PASS_COUNT PASS, $FAIL_COUNT FAIL ==="
|
|
174
|
+
|
|
175
|
+
# Write results file for reflection
|
|
176
|
+
cat > "$RESULTS_FILE" <<EOF
|
|
177
|
+
# Smoke Test Results — OrderBy
|
|
178
|
+
# Run: $(date)
|
|
179
|
+
# Pass: $PASS_COUNT / 4
|
|
180
|
+
# Fail: $FAIL_COUNT / 4
|
|
181
|
+
|
|
182
|
+
$DETAILS
|
|
183
|
+
EOF
|
|
184
|
+
|
|
185
|
+
echo "Results written to $RESULTS_FILE"
|
|
186
|
+
|
|
187
|
+
if [ "$FAIL_COUNT" -gt 0 ]; then
|
|
188
|
+
exit 1
|
|
189
|
+
fi
|
|
190
|
+
exit 0
|
|
@@ -0,0 +1,46 @@
|
|
|
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 path from "node:path";
|
|
8
|
+
import { type GraphQLSchema, introspectionFromSchema } from "graphql";
|
|
9
|
+
import { atomicWriteJson } from "../../lib/fs-utils.js";
|
|
10
|
+
import {
|
|
11
|
+
schemaCacheKeyForInstanceUrl,
|
|
12
|
+
schemaDir,
|
|
13
|
+
type SchemaMetadata,
|
|
14
|
+
} from "../../lib/introspect.js";
|
|
15
|
+
import { type PrimeDeps } from "../../lib/prime-schema.js";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Build a `PrimeDeps` whose `downloadSchema` writes the supplied schema to
|
|
19
|
+
* `schemaDir()` — redirected to a tmpdir by the global test setup — so
|
|
20
|
+
* intent-layer tests exercise the priming hook without keychain or network
|
|
21
|
+
* access. Mirrors the real `downloadSchema` contract.
|
|
22
|
+
*/
|
|
23
|
+
export function makeNoopPrimeDeps(org: string, orgUrl: string, schema: GraphQLSchema): PrimeDeps {
|
|
24
|
+
return {
|
|
25
|
+
getOrgAuth: async () => ({
|
|
26
|
+
alias: org,
|
|
27
|
+
username: "u",
|
|
28
|
+
instanceUrl: orgUrl,
|
|
29
|
+
accessToken: "t",
|
|
30
|
+
orgId: "00D",
|
|
31
|
+
}),
|
|
32
|
+
downloadSchema: async (auth) => {
|
|
33
|
+
const cacheKey = schemaCacheKeyForInstanceUrl(auth.instanceUrl);
|
|
34
|
+
const filePath = path.join(schemaDir(), `${cacheKey}.json`);
|
|
35
|
+
atomicWriteJson(filePath, { data: introspectionFromSchema(schema) });
|
|
36
|
+
const meta: SchemaMetadata = {
|
|
37
|
+
cacheKey,
|
|
38
|
+
instanceUrl: auth.instanceUrl,
|
|
39
|
+
typeCount: 0,
|
|
40
|
+
downloadedAt: new Date().toISOString(),
|
|
41
|
+
filePath,
|
|
42
|
+
};
|
|
43
|
+
return meta;
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
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 { buildSchema } from "graphql";
|
|
8
|
+
import { createSession } from "../../lib/session.js";
|
|
9
|
+
import { primeSchemaCache } from "../../lib/walker.js";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Test schema covering the union of types referenced across legacy graphiti
|
|
13
|
+
* test files. Intentionally minimal — extend only when new tests need new types.
|
|
14
|
+
*/
|
|
15
|
+
export const TEST_SCHEMA = buildSchema(`
|
|
16
|
+
type Query {
|
|
17
|
+
viewer: Viewer!
|
|
18
|
+
accounts(first: Int, where: AccountFilter): AccountConnection!
|
|
19
|
+
search: [SearchHit!]!
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
type Mutation {
|
|
23
|
+
createAccount(input: CreateAccountInput!): Account!
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
type Viewer {
|
|
27
|
+
id: ID!
|
|
28
|
+
name: String!
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
input AccountFilter {
|
|
32
|
+
minRevenue: Int
|
|
33
|
+
name: StringFilter
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
input StringFilter {
|
|
37
|
+
eq: String
|
|
38
|
+
like: String
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
input CreateAccountInput {
|
|
42
|
+
name: String!
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
type AccountConnection {
|
|
46
|
+
edges: [AccountEdge!]!
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
type AccountEdge {
|
|
50
|
+
node: Account!
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
type Account {
|
|
54
|
+
id: ID!
|
|
55
|
+
name: String!
|
|
56
|
+
annualRevenue: Float
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
type Contact {
|
|
60
|
+
id: ID!
|
|
61
|
+
name: String!
|
|
62
|
+
email: String
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
union SearchHit = Account | Contact
|
|
66
|
+
`);
|
|
67
|
+
|
|
68
|
+
export function makeSession(operation: "query" | "mutation" = "query") {
|
|
69
|
+
const alias = `test_${Math.random().toString(16).slice(2, 8)}`;
|
|
70
|
+
const instanceUrl = `https://${alias}.my.salesforce.com`;
|
|
71
|
+
primeSchemaCache(instanceUrl, TEST_SCHEMA);
|
|
72
|
+
return createSession(alias, operation, instanceUrl);
|
|
73
|
+
}
|