@rubytech/create-maxy-code 0.1.311 → 0.1.313
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/package.json +1 -1
- package/payload/platform/lib/graph-style/dist/index.d.ts +5 -2
- package/payload/platform/lib/graph-style/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/graph-style/dist/index.js +65 -2
- package/payload/platform/lib/graph-style/dist/index.js.map +1 -1
- package/payload/platform/lib/graph-style/src/__tests__/parity.test.ts +218 -0
- package/payload/platform/lib/graph-style/src/index.ts +53 -2
- package/payload/platform/plugins/.claude-plugin/marketplace.json +5 -0
- package/payload/platform/plugins/admin/hooks/lib/maxy-mcp-plugins.txt +1 -0
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +56 -4
- package/payload/platform/plugins/docs/references/admin-ui.md +21 -3
- package/payload/platform/plugins/docs/references/quickbooks.md +29 -0
- package/payload/platform/plugins/quickbooks/.claude-plugin/plugin.json +21 -0
- package/payload/platform/plugins/quickbooks/PLUGIN.md +91 -0
- package/payload/platform/plugins/quickbooks/lib/mcp-spawn-tee/index.js +159 -0
- package/payload/platform/plugins/quickbooks/lib/mcp-spawn-tee/package.json +3 -0
- package/payload/platform/plugins/quickbooks/mcp/dist/index.d.ts +2 -0
- package/payload/platform/plugins/quickbooks/mcp/dist/index.d.ts.map +1 -0
- package/payload/platform/plugins/quickbooks/mcp/dist/index.js +218 -0
- package/payload/platform/plugins/quickbooks/mcp/dist/index.js.map +1 -0
- package/payload/platform/plugins/quickbooks/mcp/dist/lib/oauth.d.ts +37 -0
- package/payload/platform/plugins/quickbooks/mcp/dist/lib/oauth.d.ts.map +1 -0
- package/payload/platform/plugins/quickbooks/mcp/dist/lib/oauth.js +75 -0
- package/payload/platform/plugins/quickbooks/mcp/dist/lib/oauth.js.map +1 -0
- package/payload/platform/plugins/quickbooks/mcp/dist/lib/qbo-client.d.ts +39 -0
- package/payload/platform/plugins/quickbooks/mcp/dist/lib/qbo-client.d.ts.map +1 -0
- package/payload/platform/plugins/quickbooks/mcp/dist/lib/qbo-client.js +178 -0
- package/payload/platform/plugins/quickbooks/mcp/dist/lib/qbo-client.js.map +1 -0
- package/payload/platform/plugins/quickbooks/mcp/dist/lib/secrets.d.ts +36 -0
- package/payload/platform/plugins/quickbooks/mcp/dist/lib/secrets.d.ts.map +1 -0
- package/payload/platform/plugins/quickbooks/mcp/dist/lib/secrets.js +107 -0
- package/payload/platform/plugins/quickbooks/mcp/dist/lib/secrets.js.map +1 -0
- package/payload/platform/plugins/quickbooks/mcp/package.json +19 -0
- package/payload/platform/plugins/quickbooks/skills/quickbooks/SKILL.md +37 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +16 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/reseat-ledger.d.ts +10 -0
- package/payload/platform/services/claude-session-manager/dist/reseat-ledger.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/reseat-ledger.js +62 -53
- package/payload/platform/services/claude-session-manager/dist/reseat-ledger.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/scope-record.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/scope-record.js +35 -53
- package/payload/platform/services/claude-session-manager/dist/scope-record.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/sidecar-store.d.ts +45 -0
- package/payload/platform/services/claude-session-manager/dist/sidecar-store.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/sidecar-store.js +118 -0
- package/payload/platform/services/claude-session-manager/dist/sidecar-store.js.map +1 -0
- package/payload/server/{chunk-J6WDAWFJ.js → chunk-Q7EWROVN.js} +4 -0
- package/payload/server/maxy-edge.js +1 -1
- package/payload/server/public/assets/{AccessGate-ChFLk-rp.js → AccessGate-Byskkf47.js} +1 -1
- package/payload/server/public/assets/{AdminLoginScreens-DU6zgbk5.js → AdminLoginScreens-DgwQi3HN.js} +1 -1
- package/payload/server/public/assets/AdminShell-DQfL2c_L.js +1 -0
- package/payload/server/public/assets/{Checkbox-CQGLKMXu.js → Checkbox-C54JSreC.js} +1 -1
- package/payload/server/public/assets/OperatorConversations-DulQyF8c.js +1 -0
- package/payload/server/public/assets/{admin-COHPIWdb.js → admin-B25rkI9Q.js} +1 -1
- package/payload/server/public/assets/{audio-attachment-mime-DL06xo72.js → audio-attachment-mime-BlwrgPQa.js} +1 -1
- package/payload/server/public/assets/{brand-CN7uSSKl.css → brand-TWflEl22.css} +1 -1
- package/payload/server/public/assets/{browser-L7N4tAOl.js → browser-C9Z5dLlN.js} +1 -1
- package/payload/server/public/assets/chat-CGNWDmid.js +1 -0
- package/payload/server/public/assets/data-DCQDh0-Y.js +1 -0
- package/payload/server/public/assets/graph-labels-BBtL--_M.js +1 -0
- package/payload/server/public/assets/{graph-Cro3-mgY.js → graph-sMzOI71g.js} +2 -2
- package/payload/server/public/assets/operator-hI6kJi0I.js +1 -0
- package/payload/server/public/assets/page-DbsqlpuX.js +1 -0
- package/payload/server/public/assets/{public-CX148JKi.js → public-Cs5N4-Az.js} +1 -1
- package/payload/server/public/assets/{public-next-BCItsUbZ.js → public-next-DoGPYX6T.js} +1 -1
- package/payload/server/public/assets/{useSelectionMode-BHAAhkDg.js → useSelectionMode-Cs-vtuKI.js} +1 -1
- package/payload/server/public/browser.html +6 -6
- package/payload/server/public/chat.html +8 -8
- package/payload/server/public/data.html +5 -5
- package/payload/server/public/graph.html +8 -8
- package/payload/server/public/index.html +8 -8
- package/payload/server/public/operator.html +10 -10
- package/payload/server/public/public-next.html +9 -9
- package/payload/server/public/public.html +7 -7
- package/payload/server/server.js +1091 -594
- package/payload/server/public/assets/AdminShell-BJCYb1v5.js +0 -1
- package/payload/server/public/assets/OperatorConversations-CHEQTz20.js +0 -1
- package/payload/server/public/assets/chat-B_jYIH9a.js +0 -1
- package/payload/server/public/assets/data-BmSTKHR1.js +0 -1
- package/payload/server/public/assets/graph-labels-WQQn6JIJ.js +0 -1
- package/payload/server/public/assets/operator-B4I25isQ.js +0 -1
- package/payload/server/public/assets/page-B-6ZQ4JG.js +0 -1
- /package/payload/server/public/assets/{brand-MKGM8WZp.js → brand-CYjs10m-.js} +0 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/** OAuth consent screen. The operator's browser lands here; Intuit redirects
|
|
2
|
+
* back to redirect_uri with code+state+realmId. */
|
|
3
|
+
export declare const INTUIT_AUTHORIZE_URL = "https://appcenter.intuit.com/connect/oauth2";
|
|
4
|
+
/** Token endpoint — same host for sandbox and production (the QBO API base
|
|
5
|
+
* differs by environment, the token endpoint does not). */
|
|
6
|
+
export declare const INTUIT_TOKEN_URL = "https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer";
|
|
7
|
+
/** Accounting scope. openid/profile/email are intentionally omitted — the
|
|
8
|
+
* connector needs company data, not an Intuit identity. */
|
|
9
|
+
export declare const QBO_SCOPE = "com.intuit.quickbooks.accounting";
|
|
10
|
+
export interface TokenResponse {
|
|
11
|
+
accessToken: string;
|
|
12
|
+
refreshToken: string;
|
|
13
|
+
/** seconds until the access token expires (Intuit returns ~3600). */
|
|
14
|
+
expiresIn: number;
|
|
15
|
+
/** seconds until the refresh token expires (Intuit returns ~8640000 ≈ 100d). */
|
|
16
|
+
refreshTokenExpiresIn: number;
|
|
17
|
+
}
|
|
18
|
+
export declare function buildAuthorizeUrl(opts: {
|
|
19
|
+
clientId: string;
|
|
20
|
+
redirectUri: string;
|
|
21
|
+
state: string;
|
|
22
|
+
scope?: string;
|
|
23
|
+
}): string;
|
|
24
|
+
export declare function exchangeCode(opts: {
|
|
25
|
+
clientId: string;
|
|
26
|
+
clientSecret: string;
|
|
27
|
+
code: string;
|
|
28
|
+
redirectUri: string;
|
|
29
|
+
tokenUrl?: string;
|
|
30
|
+
}): Promise<TokenResponse>;
|
|
31
|
+
export declare function refreshAccess(opts: {
|
|
32
|
+
clientId: string;
|
|
33
|
+
clientSecret: string;
|
|
34
|
+
refreshToken: string;
|
|
35
|
+
tokenUrl?: string;
|
|
36
|
+
}): Promise<TokenResponse>;
|
|
37
|
+
//# sourceMappingURL=oauth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth.d.ts","sourceRoot":"","sources":["../../src/lib/oauth.ts"],"names":[],"mappings":"AAOA;oDACoD;AACpD,eAAO,MAAM,oBAAoB,gDAAgD,CAAC;AAElF;4DAC4D;AAC5D,eAAO,MAAM,gBAAgB,8DACgC,CAAC;AAE9D;4DAC4D;AAC5D,eAAO,MAAM,SAAS,qCAAqC,CAAC;AAE5D,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,qEAAqE;IACrE,SAAS,EAAE,MAAM,CAAC;IAClB,gFAAgF;IAChF,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,MAAM,CAST;AAiDD,wBAAgB,YAAY,CAAC,IAAI,EAAE;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,aAAa,CAAC,CAOzB;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,aAAa,CAAC,CAMzB"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// Intuit OAuth 2.0 — authorization-code grant + refresh. Pure HTTP, no state.
|
|
2
|
+
//
|
|
3
|
+
// Two irreducibly human steps live OUTSIDE this module (relayed, never
|
|
4
|
+
// automated): the operator registers their own Intuit app, and a human clicks
|
|
5
|
+
// the authorize URL to consent per company. Everything here — the URL string,
|
|
6
|
+
// the code->token exchange, the refresh — is deterministic.
|
|
7
|
+
/** OAuth consent screen. The operator's browser lands here; Intuit redirects
|
|
8
|
+
* back to redirect_uri with code+state+realmId. */
|
|
9
|
+
export const INTUIT_AUTHORIZE_URL = "https://appcenter.intuit.com/connect/oauth2";
|
|
10
|
+
/** Token endpoint — same host for sandbox and production (the QBO API base
|
|
11
|
+
* differs by environment, the token endpoint does not). */
|
|
12
|
+
export const INTUIT_TOKEN_URL = "https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer";
|
|
13
|
+
/** Accounting scope. openid/profile/email are intentionally omitted — the
|
|
14
|
+
* connector needs company data, not an Intuit identity. */
|
|
15
|
+
export const QBO_SCOPE = "com.intuit.quickbooks.accounting";
|
|
16
|
+
export function buildAuthorizeUrl(opts) {
|
|
17
|
+
const params = new URLSearchParams({
|
|
18
|
+
client_id: opts.clientId,
|
|
19
|
+
redirect_uri: opts.redirectUri,
|
|
20
|
+
response_type: "code",
|
|
21
|
+
scope: opts.scope ?? QBO_SCOPE,
|
|
22
|
+
state: opts.state,
|
|
23
|
+
});
|
|
24
|
+
return `${INTUIT_AUTHORIZE_URL}?${params.toString()}`;
|
|
25
|
+
}
|
|
26
|
+
function basicAuth(clientId, clientSecret) {
|
|
27
|
+
return "Basic " + Buffer.from(`${clientId}:${clientSecret}`).toString("base64");
|
|
28
|
+
}
|
|
29
|
+
async function postToken(tokenUrl, clientId, clientSecret, form) {
|
|
30
|
+
const res = await fetch(tokenUrl, {
|
|
31
|
+
method: "POST",
|
|
32
|
+
headers: {
|
|
33
|
+
Authorization: basicAuth(clientId, clientSecret),
|
|
34
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
35
|
+
Accept: "application/json",
|
|
36
|
+
},
|
|
37
|
+
body: form.toString(),
|
|
38
|
+
});
|
|
39
|
+
const text = await res.text();
|
|
40
|
+
if (!res.ok) {
|
|
41
|
+
throw new Error(`token endpoint ${res.status}: ${text}`);
|
|
42
|
+
}
|
|
43
|
+
let body;
|
|
44
|
+
try {
|
|
45
|
+
body = JSON.parse(text);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
throw new Error(`token endpoint returned non-JSON: ${text}`);
|
|
49
|
+
}
|
|
50
|
+
if (!body.access_token || !body.refresh_token) {
|
|
51
|
+
throw new Error(`token endpoint response missing tokens: ${text}`);
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
accessToken: body.access_token,
|
|
55
|
+
refreshToken: body.refresh_token,
|
|
56
|
+
expiresIn: body.expires_in ?? 3600,
|
|
57
|
+
refreshTokenExpiresIn: body.x_refresh_token_expires_in ?? 8_640_000,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
export function exchangeCode(opts) {
|
|
61
|
+
const form = new URLSearchParams({
|
|
62
|
+
grant_type: "authorization_code",
|
|
63
|
+
code: opts.code,
|
|
64
|
+
redirect_uri: opts.redirectUri,
|
|
65
|
+
});
|
|
66
|
+
return postToken(opts.tokenUrl ?? INTUIT_TOKEN_URL, opts.clientId, opts.clientSecret, form);
|
|
67
|
+
}
|
|
68
|
+
export function refreshAccess(opts) {
|
|
69
|
+
const form = new URLSearchParams({
|
|
70
|
+
grant_type: "refresh_token",
|
|
71
|
+
refresh_token: opts.refreshToken,
|
|
72
|
+
});
|
|
73
|
+
return postToken(opts.tokenUrl ?? INTUIT_TOKEN_URL, opts.clientId, opts.clientSecret, form);
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=oauth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth.js","sourceRoot":"","sources":["../../src/lib/oauth.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,EAAE;AACF,uEAAuE;AACvE,8EAA8E;AAC9E,8EAA8E;AAC9E,4DAA4D;AAE5D;oDACoD;AACpD,MAAM,CAAC,MAAM,oBAAoB,GAAG,6CAA6C,CAAC;AAElF;4DAC4D;AAC5D,MAAM,CAAC,MAAM,gBAAgB,GAC3B,2DAA2D,CAAC;AAE9D;4DAC4D;AAC5D,MAAM,CAAC,MAAM,SAAS,GAAG,kCAAkC,CAAC;AAW5D,MAAM,UAAU,iBAAiB,CAAC,IAKjC;IACC,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC;QACjC,SAAS,EAAE,IAAI,CAAC,QAAQ;QACxB,YAAY,EAAE,IAAI,CAAC,WAAW;QAC9B,aAAa,EAAE,MAAM;QACrB,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,SAAS;QAC9B,KAAK,EAAE,IAAI,CAAC,KAAK;KAClB,CAAC,CAAC;IACH,OAAO,GAAG,oBAAoB,IAAI,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;AACxD,CAAC;AASD,SAAS,SAAS,CAAC,QAAgB,EAAE,YAAoB;IACvD,OAAO,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,QAAQ,IAAI,YAAY,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AAClF,CAAC;AAED,KAAK,UAAU,SAAS,CACtB,QAAgB,EAChB,QAAgB,EAChB,YAAoB,EACpB,IAAqB;IAErB,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,QAAQ,EAAE;QAChC,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,aAAa,EAAE,SAAS,CAAC,QAAQ,EAAE,YAAY,CAAC;YAChD,cAAc,EAAE,mCAAmC;YACnD,MAAM,EAAE,kBAAkB;SAC3B;QACD,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE;KACtB,CAAC,CAAC;IACH,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;IAC9B,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,kBAAkB,GAAG,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC,CAAC;IAC3D,CAAC;IACD,IAAI,IAAkB,CAAC;IACvB,IAAI,CAAC;QACH,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAiB,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,qCAAqC,IAAI,EAAE,CAAC,CAAC;IAC/D,CAAC;IACD,IAAI,CAAC,IAAI,CAAC,YAAY,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;QAC9C,MAAM,IAAI,KAAK,CAAC,2CAA2C,IAAI,EAAE,CAAC,CAAC;IACrE,CAAC;IACD,OAAO;QACL,WAAW,EAAE,IAAI,CAAC,YAAY;QAC9B,YAAY,EAAE,IAAI,CAAC,aAAa;QAChC,SAAS,EAAE,IAAI,CAAC,UAAU,IAAI,IAAI;QAClC,qBAAqB,EAAE,IAAI,CAAC,0BAA0B,IAAI,SAAS;KACpE,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAM5B;IACC,MAAM,IAAI,GAAG,IAAI,eAAe,CAAC;QAC/B,UAAU,EAAE,oBAAoB;QAChC,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,YAAY,EAAE,IAAI,CAAC,WAAW;KAC/B,CAAC,CAAC;IACH,OAAO,SAAS,CAAC,IAAI,CAAC,QAAQ,IAAI,gBAAgB,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;AAC9F,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,IAK7B;IACC,MAAM,IAAI,GAAG,IAAI,eAAe,CAAC;QAC/B,UAAU,EAAE,eAAe;QAC3B,aAAa,EAAE,IAAI,CAAC,YAAY;KACjC,CAAC,CAAC;IACH,OAAO,SAAS,CAAC,IAAI,CAAC,QAAQ,IAAI,gBAAgB,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;AAC9F,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export interface QboClientConfig {
|
|
2
|
+
/** Override Intuit's token endpoint (tests). */
|
|
3
|
+
tokenUrl?: string;
|
|
4
|
+
/** Override the QBO API base (tests); defaults from store.environment. */
|
|
5
|
+
apiBase?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function qboQuery(args: {
|
|
8
|
+
platformRoot: string;
|
|
9
|
+
accountId: string;
|
|
10
|
+
realmId: string;
|
|
11
|
+
query: string;
|
|
12
|
+
}, cfg?: QboClientConfig): Promise<unknown>;
|
|
13
|
+
export declare function qboCreate(args: {
|
|
14
|
+
platformRoot: string;
|
|
15
|
+
accountId: string;
|
|
16
|
+
realmId: string;
|
|
17
|
+
entity: string;
|
|
18
|
+
body: unknown;
|
|
19
|
+
}, cfg?: QboClientConfig): Promise<unknown>;
|
|
20
|
+
export declare function qboUpdate(args: {
|
|
21
|
+
platformRoot: string;
|
|
22
|
+
accountId: string;
|
|
23
|
+
realmId: string;
|
|
24
|
+
entity: string;
|
|
25
|
+
body: unknown;
|
|
26
|
+
}, cfg?: QboClientConfig): Promise<unknown>;
|
|
27
|
+
export declare function auditConnection(platformRoot: string, accountId: string, realmId: string, cfg?: QboClientConfig): Promise<{
|
|
28
|
+
alive: boolean;
|
|
29
|
+
ageDays: number;
|
|
30
|
+
outcome: string;
|
|
31
|
+
}>;
|
|
32
|
+
export declare function qboReport(args: {
|
|
33
|
+
platformRoot: string;
|
|
34
|
+
accountId: string;
|
|
35
|
+
realmId: string;
|
|
36
|
+
report: string;
|
|
37
|
+
params?: Record<string, string>;
|
|
38
|
+
}, cfg?: QboClientConfig): Promise<unknown>;
|
|
39
|
+
//# sourceMappingURL=qbo-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qbo-client.d.ts","sourceRoot":"","sources":["../../src/lib/qbo-client.ts"],"names":[],"mappings":"AAgBA,MAAM,WAAW,eAAe;IAC9B,gDAAgD;IAChD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAgID,wBAAgB,QAAQ,CACtB,IAAI,EAAE;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EACjF,GAAG,GAAE,eAAoB,GACxB,OAAO,CAAC,OAAO,CAAC,CAalB;AAED,wBAAgB,SAAS,CACvB,IAAI,EAAE;IACJ,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;CACf,EACD,GAAG,GAAE,eAAoB,GACxB,OAAO,CAAC,OAAO,CAAC,CAalB;AAID,wBAAgB,SAAS,CACvB,IAAI,EAAE;IACJ,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;CACf,EACD,GAAG,GAAE,eAAoB,GACxB,OAAO,CAAC,OAAO,CAAC,CAElB;AAOD,wBAAsB,eAAe,CACnC,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,GAAG,GAAE,eAAoB,GACxB,OAAO,CAAC;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAiC/D;AAED,wBAAgB,SAAS,CACvB,IAAI,EAAE;IACJ,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC,EACD,GAAG,GAAE,eAAoB,GACxB,OAAO,CAAC,OAAO,CAAC,CAalB"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
// QuickBooks Online REST client.
|
|
2
|
+
//
|
|
3
|
+
// Owns the deterministic half of the OAuth lifecycle: it keeps a short-lived
|
|
4
|
+
// access token fresh by refreshing from the stored refresh token, and persists
|
|
5
|
+
// the rotated refresh token Intuit returns on every refresh. No human is in
|
|
6
|
+
// this path. Emits the op=refresh and op=call observability lines.
|
|
7
|
+
import { readStore, upsertConnection } from "./secrets.js";
|
|
8
|
+
import { refreshAccess } from "./oauth.js";
|
|
9
|
+
const SANDBOX_API_BASE = "https://sandbox-quickbooks.api.intuit.com";
|
|
10
|
+
const PRODUCTION_API_BASE = "https://quickbooks.api.intuit.com";
|
|
11
|
+
/** Refresh when the cached access token is within this window of expiry. */
|
|
12
|
+
const ACCESS_REFRESH_THRESHOLD_MS = 60_000;
|
|
13
|
+
// Per (account, realm) access-token cache. The MCP server is one long-lived
|
|
14
|
+
// process per account, so caching here avoids a refresh on every call.
|
|
15
|
+
const accessCache = new Map();
|
|
16
|
+
function cacheKey(accountId, realmId) {
|
|
17
|
+
return `${accountId}:${realmId}`;
|
|
18
|
+
}
|
|
19
|
+
function classifyRefreshError(message) {
|
|
20
|
+
if (message.includes("invalid_grant"))
|
|
21
|
+
return "invalid_grant";
|
|
22
|
+
if (message.includes("token endpoint 401"))
|
|
23
|
+
return "401";
|
|
24
|
+
return "error";
|
|
25
|
+
}
|
|
26
|
+
async function ensureAccessToken(platformRoot, accountId, realmId, cfg) {
|
|
27
|
+
const store = readStore(platformRoot, accountId);
|
|
28
|
+
if (!store)
|
|
29
|
+
throw new Error("no QuickBooks credentials configured for this account");
|
|
30
|
+
const conn = store.connections[realmId];
|
|
31
|
+
if (!conn)
|
|
32
|
+
throw new Error(`no QuickBooks connection for realmId=${realmId}`);
|
|
33
|
+
const apiBase = cfg.apiBase ?? (store.environment === "production" ? PRODUCTION_API_BASE : SANDBOX_API_BASE);
|
|
34
|
+
const key = cacheKey(accountId, realmId);
|
|
35
|
+
const cached = accessCache.get(key);
|
|
36
|
+
if (cached && cached.expiresAt - ACCESS_REFRESH_THRESHOLD_MS > Date.now()) {
|
|
37
|
+
return { accessToken: cached.accessToken, apiBase };
|
|
38
|
+
}
|
|
39
|
+
const started = Date.now();
|
|
40
|
+
try {
|
|
41
|
+
const tok = await refreshAccess({
|
|
42
|
+
clientId: store.clientId,
|
|
43
|
+
clientSecret: store.clientSecret,
|
|
44
|
+
refreshToken: conn.refreshToken,
|
|
45
|
+
tokenUrl: cfg.tokenUrl,
|
|
46
|
+
});
|
|
47
|
+
const now = Date.now();
|
|
48
|
+
// Intuit rotates the refresh token on every refresh — persist the new one
|
|
49
|
+
// or the next refresh fails with invalid_grant.
|
|
50
|
+
upsertConnection(platformRoot, accountId, realmId, {
|
|
51
|
+
refreshToken: tok.refreshToken,
|
|
52
|
+
refreshTokenExpiry: now + tok.refreshTokenExpiresIn * 1000,
|
|
53
|
+
lastRefresh: now,
|
|
54
|
+
});
|
|
55
|
+
accessCache.set(key, { accessToken: tok.accessToken, expiresAt: now + tok.expiresIn * 1000 });
|
|
56
|
+
process.stderr.write(`[quickbooks] op=refresh realmId=${realmId} outcome=ok ms=${now - started}\n`);
|
|
57
|
+
return { accessToken: tok.accessToken, apiBase };
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
61
|
+
const outcome = classifyRefreshError(message);
|
|
62
|
+
process.stderr.write(`[quickbooks] op=refresh realmId=${realmId} outcome=${outcome} ms=${Date.now() - started}\n`);
|
|
63
|
+
throw err;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async function qboRequest(args, cfg = {}) {
|
|
67
|
+
const { accessToken, apiBase } = await ensureAccessToken(args.platformRoot, args.accountId, args.realmId, cfg);
|
|
68
|
+
// Encode with encodeURIComponent (→ %20 for spaces) rather than
|
|
69
|
+
// URLSearchParams (→ + for spaces): the QBO query endpoint expects %20 and a
|
|
70
|
+
// + would be read as a literal plus inside a SQL-ish WHERE clause.
|
|
71
|
+
const query = { minorversion: "73", ...(args.search ?? {}) };
|
|
72
|
+
const search = Object.entries(query)
|
|
73
|
+
.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
|
|
74
|
+
.join("&");
|
|
75
|
+
const url = `${apiBase}/v3/company/${args.realmId}/${args.urlPath}?${search}`;
|
|
76
|
+
const headers = {
|
|
77
|
+
Authorization: `Bearer ${accessToken}`,
|
|
78
|
+
Accept: "application/json",
|
|
79
|
+
};
|
|
80
|
+
if (args.body !== undefined)
|
|
81
|
+
headers["Content-Type"] = "application/json";
|
|
82
|
+
const started = Date.now();
|
|
83
|
+
const res = await fetch(url, {
|
|
84
|
+
method: args.method,
|
|
85
|
+
headers,
|
|
86
|
+
body: args.body !== undefined ? JSON.stringify(args.body) : undefined,
|
|
87
|
+
});
|
|
88
|
+
const text = await res.text();
|
|
89
|
+
process.stderr.write(`[quickbooks] op=call realmId=${args.realmId} entity=${args.entity} method=${args.method} status=${res.status} ms=${Date.now() - started}\n`);
|
|
90
|
+
if (!res.ok) {
|
|
91
|
+
throw new Error(`QBO ${args.method} ${args.urlPath} ${res.status}: ${text}`);
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
return JSON.parse(text);
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
throw new Error(`QBO ${args.urlPath} returned non-JSON: ${text}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
export function qboQuery(args, cfg = {}) {
|
|
101
|
+
return qboRequest({
|
|
102
|
+
platformRoot: args.platformRoot,
|
|
103
|
+
accountId: args.accountId,
|
|
104
|
+
realmId: args.realmId,
|
|
105
|
+
method: "GET",
|
|
106
|
+
urlPath: "query",
|
|
107
|
+
search: { query: args.query },
|
|
108
|
+
entity: "query",
|
|
109
|
+
}, cfg);
|
|
110
|
+
}
|
|
111
|
+
export function qboCreate(args, cfg = {}) {
|
|
112
|
+
return qboRequest({
|
|
113
|
+
platformRoot: args.platformRoot,
|
|
114
|
+
accountId: args.accountId,
|
|
115
|
+
realmId: args.realmId,
|
|
116
|
+
method: "POST",
|
|
117
|
+
urlPath: args.entity.toLowerCase(),
|
|
118
|
+
entity: args.entity,
|
|
119
|
+
body: args.body,
|
|
120
|
+
}, cfg);
|
|
121
|
+
}
|
|
122
|
+
// Update is the same endpoint as create — a full or sparse POST carrying Id +
|
|
123
|
+
// SyncToken. The caller assembles the body; the connector does not guess it.
|
|
124
|
+
export function qboUpdate(args, cfg = {}) {
|
|
125
|
+
return qboCreate(args, cfg);
|
|
126
|
+
}
|
|
127
|
+
// Standing no-event-failure check. A refresh token dies silently after ~100
|
|
128
|
+
// days of inactivity or operator revocation — nothing is emitted until the next
|
|
129
|
+
// call returns invalid_grant. auditConnection forces a refresh probe so a dead
|
|
130
|
+
// connection is surfaced BEFORE a customer-facing call hits it. A successful
|
|
131
|
+
// probe also keeps the token alive (refresh resets the 100-day clock).
|
|
132
|
+
export async function auditConnection(platformRoot, accountId, realmId, cfg = {}) {
|
|
133
|
+
const store = readStore(platformRoot, accountId);
|
|
134
|
+
const conn = store?.connections[realmId];
|
|
135
|
+
if (!store || !conn) {
|
|
136
|
+
process.stderr.write(`[quickbooks] op=token-audit realmId=${realmId} alive=false ageDays=-1\n`);
|
|
137
|
+
return { alive: false, ageDays: -1, outcome: "no-connection" };
|
|
138
|
+
}
|
|
139
|
+
const ageDays = conn.lastRefresh > 0 ? Math.floor((Date.now() - conn.lastRefresh) / 86_400_000) : -1;
|
|
140
|
+
try {
|
|
141
|
+
const tok = await refreshAccess({
|
|
142
|
+
clientId: store.clientId,
|
|
143
|
+
clientSecret: store.clientSecret,
|
|
144
|
+
refreshToken: conn.refreshToken,
|
|
145
|
+
tokenUrl: cfg.tokenUrl,
|
|
146
|
+
});
|
|
147
|
+
const now = Date.now();
|
|
148
|
+
upsertConnection(platformRoot, accountId, realmId, {
|
|
149
|
+
refreshToken: tok.refreshToken,
|
|
150
|
+
refreshTokenExpiry: now + tok.refreshTokenExpiresIn * 1000,
|
|
151
|
+
lastRefresh: now,
|
|
152
|
+
});
|
|
153
|
+
accessCache.set(cacheKey(accountId, realmId), {
|
|
154
|
+
accessToken: tok.accessToken,
|
|
155
|
+
expiresAt: now + tok.expiresIn * 1000,
|
|
156
|
+
});
|
|
157
|
+
process.stderr.write(`[quickbooks] op=token-audit realmId=${realmId} alive=true ageDays=${ageDays}\n`);
|
|
158
|
+
return { alive: true, ageDays, outcome: "ok" };
|
|
159
|
+
}
|
|
160
|
+
catch (err) {
|
|
161
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
162
|
+
const outcome = classifyRefreshError(message);
|
|
163
|
+
process.stderr.write(`[quickbooks] op=token-audit realmId=${realmId} alive=false ageDays=${ageDays}\n`);
|
|
164
|
+
return { alive: false, ageDays, outcome };
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
export function qboReport(args, cfg = {}) {
|
|
168
|
+
return qboRequest({
|
|
169
|
+
platformRoot: args.platformRoot,
|
|
170
|
+
accountId: args.accountId,
|
|
171
|
+
realmId: args.realmId,
|
|
172
|
+
method: "GET",
|
|
173
|
+
urlPath: `reports/${args.report}`,
|
|
174
|
+
search: args.params ?? {},
|
|
175
|
+
entity: `report:${args.report}`,
|
|
176
|
+
}, cfg);
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=qbo-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qbo-client.js","sourceRoot":"","sources":["../../src/lib/qbo-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export interface QbConnection {
|
|
2
|
+
refreshToken: string;
|
|
3
|
+
/** epoch ms when the refresh token itself expires (~100 days of inactivity). */
|
|
4
|
+
refreshTokenExpiry: number;
|
|
5
|
+
/** epoch ms of the last successful token exchange or refresh. */
|
|
6
|
+
lastRefresh: number;
|
|
7
|
+
}
|
|
8
|
+
export interface QbStore {
|
|
9
|
+
clientId: string;
|
|
10
|
+
clientSecret: string;
|
|
11
|
+
environment: "sandbox" | "production";
|
|
12
|
+
/** keyed by Intuit realmId (one company per realm). */
|
|
13
|
+
connections: Record<string, QbConnection>;
|
|
14
|
+
}
|
|
15
|
+
export interface QbPending {
|
|
16
|
+
state: string;
|
|
17
|
+
/** The exact redirect_uri used in the authorize request — the token exchange
|
|
18
|
+
* must echo it verbatim or Intuit rejects the code. */
|
|
19
|
+
redirectUri: string;
|
|
20
|
+
realmHint?: string;
|
|
21
|
+
createdAt: number;
|
|
22
|
+
expiresAt: number;
|
|
23
|
+
}
|
|
24
|
+
export declare function accountSecretsDir(platformRoot: string, accountId: string): string;
|
|
25
|
+
export declare function readStore(platformRoot: string, accountId: string): QbStore | null;
|
|
26
|
+
export declare function setCredentials(platformRoot: string, accountId: string, creds: {
|
|
27
|
+
clientId: string;
|
|
28
|
+
clientSecret: string;
|
|
29
|
+
environment: "sandbox" | "production";
|
|
30
|
+
}): void;
|
|
31
|
+
export declare function upsertConnection(platformRoot: string, accountId: string, realmId: string, conn: QbConnection): void;
|
|
32
|
+
export declare function removeConnection(platformRoot: string, accountId: string, realmId: string): void;
|
|
33
|
+
export declare function writePending(platformRoot: string, accountId: string, pending: QbPending): void;
|
|
34
|
+
export declare function readPending(platformRoot: string, accountId: string, state: string): QbPending | null;
|
|
35
|
+
export declare function deletePending(platformRoot: string, accountId: string, state: string): void;
|
|
36
|
+
//# sourceMappingURL=secrets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secrets.d.ts","sourceRoot":"","sources":["../../src/lib/secrets.ts"],"names":[],"mappings":"AAmBA,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,gFAAgF;IAChF,kBAAkB,EAAE,MAAM,CAAC;IAC3B,iEAAiE;IACjE,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,OAAO;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,SAAS,GAAG,YAAY,CAAC;IACtC,uDAAuD;IACvD,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;CAC3C;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd;4DACwD;IACxD,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AASD,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAEjF;AAiBD,wBAAgB,SAAS,CAAC,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAQjF;AAUD,wBAAgB,cAAc,CAC5B,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,SAAS,GAAG,YAAY,CAAA;CAAE,GACvF,IAAI,CAQN;AAED,wBAAgB,gBAAgB,CAC9B,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,YAAY,GACjB,IAAI,CAON;AAED,wBAAgB,gBAAgB,CAC9B,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,GACd,IAAI,CAKN;AAED,wBAAgB,YAAY,CAC1B,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,SAAS,GACjB,IAAI,CAIN;AAED,wBAAgB,WAAW,CACzB,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,GACZ,SAAS,GAAG,IAAI,CAalB;AAED,wBAAgB,aAAa,CAC3B,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,GACZ,IAAI,CAQN"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// QuickBooks Online per-account secret store.
|
|
2
|
+
//
|
|
3
|
+
// Plain 0600 JSON, NOT the encrypted outlook TokenStore. Two processes read and
|
|
4
|
+
// write this store: the MCP tool server (this package) and the UI server's
|
|
5
|
+
// callback route (platform/ui/server/routes/quickbooks.ts). They never import
|
|
6
|
+
// across the build-tree boundary — they agree on the on-disk shape defined here
|
|
7
|
+
// and in the callback route. Plain JSON keeps that contract trivially shareable;
|
|
8
|
+
// the secrets/ dir is brand-isolated and delete-protected (admin/files.ts), the
|
|
9
|
+
// same protection the cloudflare token store relies on.
|
|
10
|
+
import { mkdirSync, readFileSync, writeFileSync, existsSync, rmSync, } from "node:fs";
|
|
11
|
+
import { resolve, join } from "node:path";
|
|
12
|
+
/** A consent state nonce: URL-safe characters only, never a path fragment. */
|
|
13
|
+
const STATE_RE = /^[A-Za-z0-9_-]+$/;
|
|
14
|
+
function accountsDir(platformRoot) {
|
|
15
|
+
return resolve(platformRoot, "..", "data/accounts");
|
|
16
|
+
}
|
|
17
|
+
export function accountSecretsDir(platformRoot, accountId) {
|
|
18
|
+
return join(accountsDir(platformRoot), accountId, "secrets");
|
|
19
|
+
}
|
|
20
|
+
function storePath(platformRoot, accountId) {
|
|
21
|
+
return join(accountSecretsDir(platformRoot, accountId), "quickbooks.json");
|
|
22
|
+
}
|
|
23
|
+
function pendingDir(platformRoot, accountId) {
|
|
24
|
+
return join(accountSecretsDir(platformRoot, accountId), "quickbooks-pending");
|
|
25
|
+
}
|
|
26
|
+
function pendingPath(platformRoot, accountId, state) {
|
|
27
|
+
if (!STATE_RE.test(state)) {
|
|
28
|
+
throw new Error(`invalid consent state nonce: ${JSON.stringify(state)}`);
|
|
29
|
+
}
|
|
30
|
+
return join(pendingDir(platformRoot, accountId), `${state}.json`);
|
|
31
|
+
}
|
|
32
|
+
export function readStore(platformRoot, accountId) {
|
|
33
|
+
const file = storePath(platformRoot, accountId);
|
|
34
|
+
if (!existsSync(file))
|
|
35
|
+
return null;
|
|
36
|
+
try {
|
|
37
|
+
return JSON.parse(readFileSync(file, "utf-8"));
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function writeStore(platformRoot, accountId, store) {
|
|
44
|
+
const dir = accountSecretsDir(platformRoot, accountId);
|
|
45
|
+
mkdirSync(dir, { recursive: true });
|
|
46
|
+
writeFileSync(storePath(platformRoot, accountId), JSON.stringify(store, null, 2), {
|
|
47
|
+
mode: 0o600,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
export function setCredentials(platformRoot, accountId, creds) {
|
|
51
|
+
const prev = readStore(platformRoot, accountId);
|
|
52
|
+
writeStore(platformRoot, accountId, {
|
|
53
|
+
clientId: creds.clientId,
|
|
54
|
+
clientSecret: creds.clientSecret,
|
|
55
|
+
environment: creds.environment,
|
|
56
|
+
connections: prev?.connections ?? {},
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
export function upsertConnection(platformRoot, accountId, realmId, conn) {
|
|
60
|
+
const store = readStore(platformRoot, accountId);
|
|
61
|
+
if (!store) {
|
|
62
|
+
throw new Error("cannot store a connection before credentials are set");
|
|
63
|
+
}
|
|
64
|
+
store.connections[realmId] = conn;
|
|
65
|
+
writeStore(platformRoot, accountId, store);
|
|
66
|
+
}
|
|
67
|
+
export function removeConnection(platformRoot, accountId, realmId) {
|
|
68
|
+
const store = readStore(platformRoot, accountId);
|
|
69
|
+
if (!store)
|
|
70
|
+
return;
|
|
71
|
+
delete store.connections[realmId];
|
|
72
|
+
writeStore(platformRoot, accountId, store);
|
|
73
|
+
}
|
|
74
|
+
export function writePending(platformRoot, accountId, pending) {
|
|
75
|
+
const file = pendingPath(platformRoot, accountId, pending.state);
|
|
76
|
+
mkdirSync(pendingDir(platformRoot, accountId), { recursive: true });
|
|
77
|
+
writeFileSync(file, JSON.stringify(pending, null, 2), { mode: 0o600 });
|
|
78
|
+
}
|
|
79
|
+
export function readPending(platformRoot, accountId, state) {
|
|
80
|
+
let file;
|
|
81
|
+
try {
|
|
82
|
+
file = pendingPath(platformRoot, accountId, state);
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
if (!existsSync(file))
|
|
88
|
+
return null;
|
|
89
|
+
try {
|
|
90
|
+
return JSON.parse(readFileSync(file, "utf-8"));
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
export function deletePending(platformRoot, accountId, state) {
|
|
97
|
+
let file;
|
|
98
|
+
try {
|
|
99
|
+
file = pendingPath(platformRoot, accountId, state);
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (existsSync(file))
|
|
105
|
+
rmSync(file);
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=secrets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secrets.js","sourceRoot":"","sources":["../../src/lib/secrets.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@maxy/quickbooks",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"start": "node dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
13
|
+
"zod": "^3.24.0"
|
|
14
|
+
},
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"typescript": "^5.7.0",
|
|
17
|
+
"@types/node": "^22.0.0"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: quickbooks
|
|
3
|
+
description: Connect QuickBooks Online and work with the books. Use when the operator wants to link QuickBooks, read invoices/customers/bills/reports, or create and update accounting records. Covers the one-time Intuit app registration and per-company consent relay, then the agent-driven read/write surface.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# QuickBooks Online connector
|
|
7
|
+
|
|
8
|
+
QuickBooks Online lives in Intuit's cloud. Maxy never owns the OAuth app — the operator brings their own Intuit app, and a human consents per company. After that, reads and writes are yours.
|
|
9
|
+
|
|
10
|
+
Success is: the operator's credentials are stored, at least one company shows up under `quickbooks-connections`, and a `quickbooks-query` returns that company's data.
|
|
11
|
+
|
|
12
|
+
## The two human steps (relay these — never click for them)
|
|
13
|
+
|
|
14
|
+
**1. Register an Intuit app.** The operator (not the agent) does this once at https://developer.intuit.com. They create an app, enable the Accounting scope, and copy its **client ID** and **client secret**. In the app's Keys & OAuth settings they add a **Redirect URI** that must be exactly the brand's public site URL followed by `/api/quickbooks/callback` (for example `https://app.<brand-domain>/api/quickbooks/callback`). Intuit rejects `localhost` for production, which is why the callback rides the public host. For a first run, the sandbox keys (free, up to five sandbox companies) are the right starting point.
|
|
15
|
+
|
|
16
|
+
**2. Consent per company.** You generate the consent link with `quickbooks-authorize-url`; a human opens it, signs into Intuit, and authorizes one company. Intuit redirects their browser back to the callback, which stores the refresh token. Repeat per company for the accountant case (one Intuit app can hold many company grants).
|
|
17
|
+
|
|
18
|
+
## Procedure
|
|
19
|
+
|
|
20
|
+
1. Once the operator has the client ID and secret, store them: `quickbooks-credentials-set` with `environment` set to `sandbox` or `production`.
|
|
21
|
+
2. Generate the consent link: `quickbooks-authorize-url` with `redirectUri` set to the **exact** registered redirect URI. Hand the returned URL to a human. The link is valid 15 minutes.
|
|
22
|
+
3. After they authorize, confirm with `quickbooks-connections` — the company's `realmId` should appear.
|
|
23
|
+
4. Read and write against that `realmId`.
|
|
24
|
+
|
|
25
|
+
## Working with the books
|
|
26
|
+
|
|
27
|
+
- **Read anything** with `quickbooks-query`, e.g. `SELECT * FROM Invoice WHERE TxnDate > '2026-01-01'`, `SELECT * FROM Customer`, `SELECT * FROM Bill MAXRESULTS 50`.
|
|
28
|
+
- **Reports** with `quickbooks-report`, e.g. `ProfitAndLoss` with `{ start_date, end_date }`, or `BalanceSheet`.
|
|
29
|
+
- **Write** with the entity tools (`quickbooks-invoice-create`, `quickbooks-customer-create`, `quickbooks-bill-create`, `quickbooks-payment-create`) or the generic `quickbooks-entity-create` for anything else. Updates require the record's current `Id` and `SyncToken` in the body (read it back first with a query).
|
|
30
|
+
|
|
31
|
+
## Keeping connections healthy
|
|
32
|
+
|
|
33
|
+
A refresh token dies silently after about 100 days of inactivity or if the operator revokes it. Run `quickbooks-token-audit` periodically; `alive=false` means that company needs the consent relay run again before it is used. Reads are metered (a free monthly allowance) and writes are free — prefer a targeted query over pulling everything.
|
|
34
|
+
|
|
35
|
+
## What stays the operator's job
|
|
36
|
+
|
|
37
|
+
Moving an Intuit app from sandbox to production requires Intuit's production security assessment. That is the operator's responsibility and is not automated here.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"canonical-tool-names.generated.d.ts","sourceRoot":"","sources":["../src/canonical-tool-names.generated.ts"],"names":[],"mappings":"AAQA,+EAA+E;AAC/E,eAAO,MAAM,gBAAgB,EAAE,SAAS,MAAM,
|
|
1
|
+
{"version":3,"file":"canonical-tool-names.generated.d.ts","sourceRoot":"","sources":["../src/canonical-tool-names.generated.ts"],"names":[],"mappings":"AAQA,+EAA+E;AAC/E,eAAO,MAAM,gBAAgB,EAAE,SAAS,MAAM,EAkB7C,CAAA;AAED,gEAAgE;AAChE,eAAO,MAAM,yBAAyB,EAAE,SAAS,MAAM,EAiMtD,CAAA"}
|
package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js
CHANGED
|
@@ -16,6 +16,7 @@ export const MAXY_MCP_PLUGINS = [
|
|
|
16
16
|
"graph-viewer",
|
|
17
17
|
"memory",
|
|
18
18
|
"outlook",
|
|
19
|
+
"quickbooks",
|
|
19
20
|
"replicate",
|
|
20
21
|
"scheduling",
|
|
21
22
|
"telegram",
|
|
@@ -155,6 +156,21 @@ export const CANONICAL_MAXY_TOOL_NAMES = [
|
|
|
155
156
|
"mcp__plugin_outlook_outlook__outlook-mail-list",
|
|
156
157
|
"mcp__plugin_outlook_outlook__outlook-mail-search",
|
|
157
158
|
"mcp__plugin_outlook_outlook__outlook-mailbox-info",
|
|
159
|
+
"mcp__plugin_quickbooks_quickbooks__quickbooks-authorize-url",
|
|
160
|
+
"mcp__plugin_quickbooks_quickbooks__quickbooks-bill-create",
|
|
161
|
+
"mcp__plugin_quickbooks_quickbooks__quickbooks-connections",
|
|
162
|
+
"mcp__plugin_quickbooks_quickbooks__quickbooks-credentials-set",
|
|
163
|
+
"mcp__plugin_quickbooks_quickbooks__quickbooks-customer-create",
|
|
164
|
+
"mcp__plugin_quickbooks_quickbooks__quickbooks-customer-update",
|
|
165
|
+
"mcp__plugin_quickbooks_quickbooks__quickbooks-disconnect",
|
|
166
|
+
"mcp__plugin_quickbooks_quickbooks__quickbooks-entity-create",
|
|
167
|
+
"mcp__plugin_quickbooks_quickbooks__quickbooks-entity-update",
|
|
168
|
+
"mcp__plugin_quickbooks_quickbooks__quickbooks-invoice-create",
|
|
169
|
+
"mcp__plugin_quickbooks_quickbooks__quickbooks-invoice-update",
|
|
170
|
+
"mcp__plugin_quickbooks_quickbooks__quickbooks-payment-create",
|
|
171
|
+
"mcp__plugin_quickbooks_quickbooks__quickbooks-query",
|
|
172
|
+
"mcp__plugin_quickbooks_quickbooks__quickbooks-report",
|
|
173
|
+
"mcp__plugin_quickbooks_quickbooks__quickbooks-token-audit",
|
|
158
174
|
"mcp__plugin_replicate_replicate__image-generate",
|
|
159
175
|
"mcp__plugin_replicate_replicate__replicate-key-store",
|
|
160
176
|
"mcp__plugin_replicate_replicate__replicate-key-verify",
|
package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"canonical-tool-names.generated.js","sourceRoot":"","sources":["../src/canonical-tool-names.generated.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"canonical-tool-names.generated.js","sourceRoot":"","sources":["../src/canonical-tool-names.generated.ts"],"names":[],"mappings":"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"}
|