document360-engine 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +47 -0
  3. package/dist/config.d.ts +73 -0
  4. package/dist/config.js +62 -0
  5. package/dist/config.js.map +1 -0
  6. package/dist/d360/client.d.ts +30 -0
  7. package/dist/d360/client.js +132 -0
  8. package/dist/d360/client.js.map +1 -0
  9. package/dist/d360/environments.d.ts +28 -0
  10. package/dist/d360/environments.js +54 -0
  11. package/dist/d360/environments.js.map +1 -0
  12. package/dist/d360/oauth.d.ts +27 -0
  13. package/dist/d360/oauth.js +162 -0
  14. package/dist/d360/oauth.js.map +1 -0
  15. package/dist/d360/profile.d.ts +18 -0
  16. package/dist/d360/profile.js +31 -0
  17. package/dist/d360/profile.js.map +1 -0
  18. package/dist/d360/tokenStore.d.ts +17 -0
  19. package/dist/d360/tokenStore.js +50 -0
  20. package/dist/d360/tokenStore.js.map +1 -0
  21. package/dist/d360/tools.d.ts +14 -0
  22. package/dist/d360/tools.js +335 -0
  23. package/dist/d360/tools.js.map +1 -0
  24. package/dist/index.d.ts +11 -0
  25. package/dist/index.js +16 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/lib/auth.d.ts +14 -0
  28. package/dist/lib/auth.js +25 -0
  29. package/dist/lib/auth.js.map +1 -0
  30. package/dist/lib/messageQueue.d.ts +9 -0
  31. package/dist/lib/messageQueue.js +40 -0
  32. package/dist/lib/messageQueue.js.map +1 -0
  33. package/dist/lib/paths.d.ts +9 -0
  34. package/dist/lib/paths.js +35 -0
  35. package/dist/lib/paths.js.map +1 -0
  36. package/dist/lib/sessionStore.d.ts +24 -0
  37. package/dist/lib/sessionStore.js +100 -0
  38. package/dist/lib/sessionStore.js.map +1 -0
  39. package/dist/lib/titleGen.d.ts +7 -0
  40. package/dist/lib/titleGen.js +64 -0
  41. package/dist/lib/titleGen.js.map +1 -0
  42. package/dist/session.d.ts +47 -0
  43. package/dist/session.js +190 -0
  44. package/dist/session.js.map +1 -0
  45. package/package.json +44 -0
  46. package/skills/CLAUDE.md +92 -0
  47. package/skills/analyze-codebase/SKILL.md +35 -0
  48. package/skills/audit-docs/SKILL.md +48 -0
  49. package/skills/emit-screenshot-spec/SKILL.md +82 -0
  50. package/skills/gather-context-from-mcp/SKILL.md +29 -0
  51. package/skills/propose-structure/SKILL.md +51 -0
  52. package/skills/publish-to-d360/SKILL.md +49 -0
  53. package/skills/write-article/SKILL.md +95 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Saravana Kumar
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,47 @@
1
+ # document360-engine
2
+
3
+ Headless documentation-agent engine for [`document360-writer`](https://www.npmjs.com/package/document360-writer) and future GUI wrappers (e.g. `document360-desktop`).
4
+
5
+ This package contains **no UI**. It exposes the agent session lifecycle, Document360 OAuth/profiles/config, and an in-process Document360 tool server, and emits a typed **event stream** that wrappers render however they like (terminal, GUI). It's the documentation analog of the Claude Agent SDK: one engine, many front-ends.
6
+
7
+ > You almost certainly want `document360-writer` (the CLI), not this package directly. This is the shared core.
8
+
9
+ ## Concept
10
+
11
+ ```
12
+ document360-engine ← this package: headless, emits EngineEvents
13
+ ├─ document360-writer ← terminal wrapper (CLI + Ink TUI)
14
+ └─ document360-desktop ← (future) GUI wrapper
15
+ ```
16
+
17
+ The engine never writes to a screen. Each wrapper does two things: **render events** and **collect input → engine calls**.
18
+
19
+ ## Minimal usage
20
+
21
+ ```ts
22
+ import { createSession } from 'document360-engine';
23
+
24
+ const session = createSession({ cwd: process.cwd(), authMode: 'auto' });
25
+ for await (const event of session.send('analyze this repo and propose a docs structure')) {
26
+ switch (event.type) {
27
+ case 'text': process.stdout.write(event.delta); break; // streaming answer
28
+ case 'tool': console.error(`tool: ${event.name}`); break; // a tool was invoked
29
+ case 'result': console.error(`done: ${event.outputTokens} tokens`); break;
30
+ case 'error': console.error(`error: ${event.message}`); break;
31
+ }
32
+ }
33
+ session.close();
34
+ ```
35
+
36
+ All `EngineEvent` variants are plain JSON, so a wrapper can stream them over IPC to a GUI.
37
+
38
+ ## Also exported
39
+
40
+ - **Document360 auth**: `loginPkce`, `whoami`, token store, `resolveActiveProfile`, profile helpers.
41
+ - **Claude auth**: `resolveAuth` (`api` / `subscription` / `auto`).
42
+ - **Config**: `readProjectConfig` / `writeProjectConfig`, `resolveProfile`, types.
43
+ - **Sessions**: `createSession`, `listSessions`, `getSession`, `renameSession`, `generateTitle`.
44
+
45
+ ## License
46
+
47
+ MIT
@@ -0,0 +1,73 @@
1
+ /** How to reach a Document360 deployment. `environment` names a baked preset
2
+ (src/d360/environments.ts); any field present here overrides the preset. */
3
+ export type ProfileConnection = {
4
+ environment?: string;
5
+ apiUrl?: string;
6
+ authorizationUrl?: string;
7
+ tokenUrl?: string;
8
+ clientId?: string;
9
+ scopes?: string[];
10
+ redirectUri?: string;
11
+ acrValues?: string;
12
+ prompt?: string;
13
+ };
14
+ /** The Document360 project a profile acts on (chosen at login via project_select). */
15
+ export type ProfileProject = {
16
+ projectId?: string;
17
+ workspaceId?: string;
18
+ languageCode?: string;
19
+ };
20
+ /** A named, switchable connection profile. `connection` is one aspect; more may be added. */
21
+ export type Profile = {
22
+ connection: ProfileConnection;
23
+ project?: ProfileProject;
24
+ /** Guards writes (publish/update/upload) behind explicit confirmation. */
25
+ production?: boolean;
26
+ };
27
+ export type ProjectConfig = {
28
+ /** Writer's own label — scopes sessions/screenshots. Not the Document360 project. */
29
+ projectId: string;
30
+ captureDir?: string;
31
+ outputDir?: string;
32
+ authoritativeSourceFiles?: string[];
33
+ terminologyGlossary?: Record<string, string>;
34
+ defaultModel?: string;
35
+ /** Connection profiles. See plans/connection-profiles.md. */
36
+ profiles?: Record<string, Profile>;
37
+ defaultProfile?: string;
38
+ };
39
+ export declare class ProfileConfigError extends Error {
40
+ }
41
+ /** Resolve a profile by name (or the default). Throws a migration error if the config
42
+ predates profiles (no back-compat by design — single pattern). */
43
+ export declare function resolveProfile(cfg: ProjectConfig | null, name?: string): {
44
+ name: string;
45
+ profile: Profile;
46
+ };
47
+ export type UserConfig = {
48
+ defaultModel?: string;
49
+ autoUpdate?: boolean;
50
+ };
51
+ export type McpServerEntry = {
52
+ type: 'stdio';
53
+ command: string;
54
+ args?: string[];
55
+ env?: Record<string, string>;
56
+ } | {
57
+ type: 'http';
58
+ url: string;
59
+ headers?: Record<string, string>;
60
+ } | {
61
+ type: 'sse';
62
+ url: string;
63
+ headers?: Record<string, string>;
64
+ };
65
+ export type UserMcpConfig = {
66
+ servers: Record<string, McpServerEntry>;
67
+ };
68
+ export declare function readProjectConfig(cwd?: string): ProjectConfig | null;
69
+ export declare function writeProjectConfig(cfg: ProjectConfig, cwd?: string): void;
70
+ export declare function readUserConfig(): UserConfig;
71
+ export declare function writeUserConfig(cfg: UserConfig): void;
72
+ export declare function readMcpConfig(): UserMcpConfig;
73
+ export declare function writeMcpConfig(cfg: UserMcpConfig): void;
package/dist/config.js ADDED
@@ -0,0 +1,62 @@
1
+ import { readFileSync, writeFileSync, existsSync } from 'node:fs';
2
+ import { projectConfigPath, userConfigPath, userMcpConfigPath, userDir, ensureDir, } from './lib/paths.js';
3
+ export class ProfileConfigError extends Error {
4
+ }
5
+ /** Resolve a profile by name (or the default). Throws a migration error if the config
6
+ predates profiles (no back-compat by design — single pattern). */
7
+ export function resolveProfile(cfg, name) {
8
+ if (!cfg?.profiles || Object.keys(cfg.profiles).length === 0) {
9
+ throw new ProfileConfigError('No connection profiles in .d360-writer.json. This version requires a `profiles` map + ' +
10
+ '`defaultProfile` (the old d360.environment/projectId shape is no longer supported). ' +
11
+ 'Run `d360-writer init` to scaffold the new shape.');
12
+ }
13
+ const chosen = name ?? cfg.defaultProfile;
14
+ if (!chosen) {
15
+ throw new ProfileConfigError(`No profile specified and no defaultProfile set. Available: ${Object.keys(cfg.profiles).join(', ')}`);
16
+ }
17
+ const profile = cfg.profiles[chosen];
18
+ if (!profile) {
19
+ throw new ProfileConfigError(`Unknown profile "${chosen}". Available: ${Object.keys(cfg.profiles).join(', ')}`);
20
+ }
21
+ return { name: chosen, profile };
22
+ }
23
+ export function readProjectConfig(cwd = process.cwd()) {
24
+ const path = projectConfigPath(cwd);
25
+ if (!existsSync(path))
26
+ return null;
27
+ return JSON.parse(readFileSync(path, 'utf8'));
28
+ }
29
+ export function writeProjectConfig(cfg, cwd = process.cwd()) {
30
+ writeFileSync(projectConfigPath(cwd), JSON.stringify(cfg, null, 2) + '\n', 'utf8');
31
+ }
32
+ export function readUserConfig() {
33
+ const path = userConfigPath();
34
+ if (!existsSync(path))
35
+ return {};
36
+ try {
37
+ return JSON.parse(readFileSync(path, 'utf8'));
38
+ }
39
+ catch {
40
+ return {};
41
+ }
42
+ }
43
+ export function writeUserConfig(cfg) {
44
+ ensureDir(userDir());
45
+ writeFileSync(userConfigPath(), JSON.stringify(cfg, null, 2) + '\n', 'utf8');
46
+ }
47
+ export function readMcpConfig() {
48
+ const path = userMcpConfigPath();
49
+ if (!existsSync(path))
50
+ return { servers: {} };
51
+ try {
52
+ return JSON.parse(readFileSync(path, 'utf8'));
53
+ }
54
+ catch {
55
+ return { servers: {} };
56
+ }
57
+ }
58
+ export function writeMcpConfig(cfg) {
59
+ ensureDir(userDir());
60
+ writeFileSync(userMcpConfigPath(), JSON.stringify(cfg, null, 2) + '\n', 'utf8');
61
+ }
62
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import type { D360Environment } from './environments.js';
2
+ /** Auth/connection context for a request: which profile's tokens + which connection. */
3
+ export type D360Context = {
4
+ profile: string;
5
+ connection: D360Environment;
6
+ };
7
+ export declare class D360AuthError extends Error {
8
+ }
9
+ export declare class D360ApiError extends Error {
10
+ readonly status: number;
11
+ readonly requestId?: string | undefined;
12
+ constructor(message: string, status: number, requestId?: string | undefined);
13
+ }
14
+ /** Valid access token for the profile, refreshing if possible. Throws D360AuthError otherwise. */
15
+ export declare function getAccessToken(ctx: D360Context): Promise<string>;
16
+ /** The project the writer acts on: explicit (profile/arg) wins, else the token's doc360_project_id claim. */
17
+ export declare function resolveProjectId(ctx: D360Context, configProjectId?: string): string;
18
+ export type D360RequestOptions = {
19
+ query?: Record<string, string | number | boolean | undefined>;
20
+ body?: unknown;
21
+ };
22
+ /** Single request, returns the unwrapped `data`. */
23
+ export declare function d360Get<T>(ctx: D360Context, path: string, opts?: D360RequestOptions): Promise<T>;
24
+ export declare function d360Post<T>(ctx: D360Context, path: string, opts?: D360RequestOptions): Promise<T>;
25
+ export declare function d360Patch<T>(ctx: D360Context, path: string, opts?: D360RequestOptions): Promise<T>;
26
+ /** Multipart upload (Drive files). Does NOT set content-type — fetch derives the
27
+ multipart boundary from the FormData. Returns the unwrapped `data`. */
28
+ export declare function d360Upload<T>(ctx: D360Context, path: string, form: FormData): Promise<T>;
29
+ /** Cursor-paginated GET — follows pagination.next_cursor, concatenating data arrays. */
30
+ export declare function d360GetAll<T>(ctx: D360Context, path: string, opts?: D360RequestOptions): Promise<T[]>;
@@ -0,0 +1,132 @@
1
+ import { refreshTokens, toStoredTokens } from './oauth.js';
2
+ import { decodeJwtClaims, isExpired, loadTokens, saveTokens } from './tokenStore.js';
3
+ export class D360AuthError extends Error {
4
+ }
5
+ export class D360ApiError extends Error {
6
+ status;
7
+ requestId;
8
+ constructor(message, status, requestId) {
9
+ super(message);
10
+ this.status = status;
11
+ this.requestId = requestId;
12
+ }
13
+ }
14
+ /** Valid access token for the profile, refreshing if possible. Throws D360AuthError otherwise. */
15
+ export async function getAccessToken(ctx) {
16
+ const tokens = loadTokens(ctx.profile);
17
+ if (!tokens) {
18
+ throw new D360AuthError(`Not logged in to Document360 (profile "${ctx.profile}"). Run: d360-writer login --profile ${ctx.profile}`);
19
+ }
20
+ if (!isExpired(tokens))
21
+ return tokens.accessToken;
22
+ if (!tokens.refreshToken) {
23
+ throw new D360AuthError(`Document360 session expired (profile "${ctx.profile}"). Run: d360-writer login --profile ${ctx.profile}`);
24
+ }
25
+ try {
26
+ const refreshed = toStoredTokens(ctx.profile, await refreshTokens(ctx.connection, tokens.refreshToken));
27
+ saveTokens(refreshed);
28
+ return refreshed.accessToken;
29
+ }
30
+ catch (err) {
31
+ throw new D360AuthError(`Document360 session expired and refresh failed (${err.message.slice(0, 120)}). Run: d360-writer login --profile ${ctx.profile}`);
32
+ }
33
+ }
34
+ /** The project the writer acts on: explicit (profile/arg) wins, else the token's doc360_project_id claim. */
35
+ export function resolveProjectId(ctx, configProjectId) {
36
+ if (configProjectId)
37
+ return configProjectId;
38
+ const tokens = loadTokens(ctx.profile);
39
+ const claims = tokens ? (decodeJwtClaims(tokens.accessToken) ?? {}) : {};
40
+ const fromClaim = claims['doc360_project_id'];
41
+ if (typeof fromClaim === 'string' && fromClaim)
42
+ return fromClaim;
43
+ throw new D360AuthError('No Document360 project resolved. Log in (the project is chosen during login) or set the profile\'s project.projectId in .d360-writer.json.');
44
+ }
45
+ async function rawFetch(ctx, method, path, opts = {}) {
46
+ const token = await getAccessToken(ctx);
47
+ const url = new URL(path.startsWith('http') ? path : `${ctx.connection.apiUrl}${path}`);
48
+ for (const [k, v] of Object.entries(opts.query ?? {})) {
49
+ if (v !== undefined)
50
+ url.searchParams.set(k, String(v));
51
+ }
52
+ const res = await fetch(url, {
53
+ method,
54
+ headers: {
55
+ authorization: `Bearer ${token}`,
56
+ accept: 'application/json',
57
+ ...(opts.body !== undefined ? { 'content-type': 'application/json' } : {}),
58
+ },
59
+ body: opts.body !== undefined ? JSON.stringify(opts.body) : undefined,
60
+ });
61
+ const text = await res.text();
62
+ let envelope = {};
63
+ if (text) {
64
+ try {
65
+ envelope = JSON.parse(text);
66
+ }
67
+ catch {
68
+ /* non-JSON body — handled below */
69
+ }
70
+ }
71
+ if (!res.ok || envelope.success === false) {
72
+ const detail = envelope.errors?.map(e => e.message ?? e.code).filter(Boolean).join('; ') || text.slice(0, 300) || res.statusText;
73
+ if (res.status === 401) {
74
+ throw new D360AuthError(`Document360 rejected the token (401). Run: d360-writer login --profile ${ctx.profile}`);
75
+ }
76
+ throw new D360ApiError(`Document360 API ${res.status}: ${detail}`, res.status, envelope.request_id);
77
+ }
78
+ return envelope;
79
+ }
80
+ /** Single request, returns the unwrapped `data`. */
81
+ export async function d360Get(ctx, path, opts) {
82
+ return (await rawFetch(ctx, 'GET', path, opts)).data;
83
+ }
84
+ export async function d360Post(ctx, path, opts) {
85
+ return (await rawFetch(ctx, 'POST', path, opts)).data;
86
+ }
87
+ export async function d360Patch(ctx, path, opts) {
88
+ return (await rawFetch(ctx, 'PATCH', path, opts)).data;
89
+ }
90
+ /** Multipart upload (Drive files). Does NOT set content-type — fetch derives the
91
+ multipart boundary from the FormData. Returns the unwrapped `data`. */
92
+ export async function d360Upload(ctx, path, form) {
93
+ const token = await getAccessToken(ctx);
94
+ const res = await fetch(`${ctx.connection.apiUrl}${path}`, {
95
+ method: 'POST',
96
+ headers: { authorization: `Bearer ${token}`, accept: 'application/json' },
97
+ body: form,
98
+ });
99
+ const text = await res.text();
100
+ let envelope = {};
101
+ if (text) {
102
+ try {
103
+ envelope = JSON.parse(text);
104
+ }
105
+ catch {
106
+ /* non-JSON */
107
+ }
108
+ }
109
+ if (!res.ok || envelope.success === false) {
110
+ const detail = envelope.errors?.map(e => e.message ?? e.code).filter(Boolean).join('; ') || text.slice(0, 300) || res.statusText;
111
+ if (res.status === 401)
112
+ throw new D360AuthError(`Document360 rejected the token (401). Run: d360-writer login --profile ${ctx.profile}`);
113
+ throw new D360ApiError(`Document360 upload ${res.status}: ${detail}`, res.status, envelope.request_id);
114
+ }
115
+ return envelope.data;
116
+ }
117
+ /** Cursor-paginated GET — follows pagination.next_cursor, concatenating data arrays. */
118
+ export async function d360GetAll(ctx, path, opts = {}) {
119
+ const out = [];
120
+ let cursor;
121
+ // Safety cap: 50 pages — a runaway pagination loop shouldn't hang the agent.
122
+ for (let page = 0; page < 50; page++) {
123
+ const envelope = await rawFetch(ctx, 'GET', path, { ...opts, query: { ...opts.query, cursor } });
124
+ if (Array.isArray(envelope.data))
125
+ out.push(...envelope.data);
126
+ if (!envelope.pagination?.has_more || !envelope.pagination.next_cursor)
127
+ break;
128
+ cursor = envelope.pagination.next_cursor;
129
+ }
130
+ return out;
131
+ }
132
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/d360/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAMrF,MAAM,OAAO,aAAc,SAAQ,KAAK;CAAG;AAC3C,MAAM,OAAO,YAAa,SAAQ,KAAK;IAG1B;IACA;IAHX,YACE,OAAe,EACN,MAAc,EACd,SAAkB;QAE3B,KAAK,CAAC,OAAO,CAAC,CAAC;QAHN,WAAM,GAAN,MAAM,CAAQ;QACd,cAAS,GAAT,SAAS,CAAS;IAG7B,CAAC;CACF;AAED,kGAAkG;AAClG,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,GAAgB;IACnD,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACvC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,aAAa,CAAC,0CAA0C,GAAG,CAAC,OAAO,wCAAwC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;IACtI,CAAC;IACD,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC;QAAE,OAAO,MAAM,CAAC,WAAW,CAAC;IAClD,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC;QACzB,MAAM,IAAI,aAAa,CAAC,yCAAyC,GAAG,CAAC,OAAO,wCAAwC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;IACrI,CAAC;IACD,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,cAAc,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,aAAa,CAAC,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC;QACxG,UAAU,CAAC,SAAS,CAAC,CAAC;QACtB,OAAO,SAAS,CAAC,WAAW,CAAC;IAC/B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,aAAa,CACrB,mDAAoD,GAAa,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,uCAAuC,GAAG,CAAC,OAAO,EAAE,CAC5I,CAAC;IACJ,CAAC;AACH,CAAC;AAED,6GAA6G;AAC7G,MAAM,UAAU,gBAAgB,CAAC,GAAgB,EAAE,eAAwB;IACzE,IAAI,eAAe;QAAE,OAAO,eAAe,CAAC;IAC5C,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACvC,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACzE,MAAM,SAAS,GAAG,MAAM,CAAC,mBAAmB,CAAC,CAAC;IAC9C,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,SAAS;QAAE,OAAO,SAAS,CAAC;IACjE,MAAM,IAAI,aAAa,CACrB,4IAA4I,CAC7I,CAAC;AACJ,CAAC;AAgBD,KAAK,UAAU,QAAQ,CAAI,GAAgB,EAAE,MAAc,EAAE,IAAY,EAAE,OAA2B,EAAE;IACtG,MAAM,KAAK,GAAG,MAAM,cAAc,CAAC,GAAG,CAAC,CAAC;IACxC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,UAAU,CAAC,MAAM,GAAG,IAAI,EAAE,CAAC,CAAC;IACxF,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,EAAE,CAAC;QACtD,IAAI,CAAC,KAAK,SAAS;YAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1D,CAAC;IACD,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;QAC3B,MAAM;QACN,OAAO,EAAE;YACP,aAAa,EAAE,UAAU,KAAK,EAAE;YAChC,MAAM,EAAE,kBAAkB;YAC1B,GAAG,CAAC,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC3E;QACD,IAAI,EAAE,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;KACtE,CAAC,CAAC;IACH,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;IAC9B,IAAI,QAAQ,GAAgB,EAAE,CAAC;IAC/B,IAAI,IAAI,EAAE,CAAC;QACT,IAAI,CAAC;YACH,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAgB,CAAC;QAC7C,CAAC;QAAC,MAAM,CAAC;YACP,mCAAmC;QACrC,CAAC;IACH,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,QAAQ,CAAC,OAAO,KAAK,KAAK,EAAE,CAAC;QAC1C,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC;QACjI,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YACvB,MAAM,IAAI,aAAa,CAAC,0EAA0E,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QACnH,CAAC;QACD,MAAM,IAAI,YAAY,CAAC,mBAAmB,GAAG,CAAC,MAAM,KAAK,MAAM,EAAE,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC;IACtG,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,oDAAoD;AACpD,MAAM,CAAC,KAAK,UAAU,OAAO,CAAI,GAAgB,EAAE,IAAY,EAAE,IAAyB;IACxF,OAAO,CAAC,MAAM,QAAQ,CAAI,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,IAAS,CAAC;AAC/D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAI,GAAgB,EAAE,IAAY,EAAE,IAAyB;IACzF,OAAO,CAAC,MAAM,QAAQ,CAAI,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,IAAS,CAAC;AAChE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAI,GAAgB,EAAE,IAAY,EAAE,IAAyB;IAC1F,OAAO,CAAC,MAAM,QAAQ,CAAI,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,IAAS,CAAC;AACjE,CAAC;AAED;0EAC0E;AAC1E,MAAM,CAAC,KAAK,UAAU,UAAU,CAAI,GAAgB,EAAE,IAAY,EAAE,IAAc;IAChF,MAAM,KAAK,GAAG,MAAM,cAAc,CAAC,GAAG,CAAC,CAAC;IACxC,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,GAAG,CAAC,UAAU,CAAC,MAAM,GAAG,IAAI,EAAE,EAAE;QACzD,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,KAAK,EAAE,EAAE,MAAM,EAAE,kBAAkB,EAAE;QACzE,IAAI,EAAE,IAAI;KACX,CAAC,CAAC;IACH,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;IAC9B,IAAI,QAAQ,GAAgB,EAAE,CAAC;IAC/B,IAAI,IAAI,EAAE,CAAC;QACT,IAAI,CAAC;YACH,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAgB,CAAC;QAC7C,CAAC;QAAC,MAAM,CAAC;YACP,cAAc;QAChB,CAAC;IACH,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,QAAQ,CAAC,OAAO,KAAK,KAAK,EAAE,CAAC;QAC1C,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC;QACjI,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG;YAAE,MAAM,IAAI,aAAa,CAAC,0EAA0E,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QACzI,MAAM,IAAI,YAAY,CAAC,sBAAsB,GAAG,CAAC,MAAM,KAAK,MAAM,EAAE,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC;IACzG,CAAC;IACD,OAAO,QAAQ,CAAC,IAAS,CAAC;AAC5B,CAAC;AAED,wFAAwF;AACxF,MAAM,CAAC,KAAK,UAAU,UAAU,CAAI,GAAgB,EAAE,IAAY,EAAE,OAA2B,EAAE;IAC/F,MAAM,GAAG,GAAQ,EAAE,CAAC;IACpB,IAAI,MAA0B,CAAC;IAC/B,6EAA6E;IAC7E,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC;QACrC,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAM,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;QACtG,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,GAAG,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC7D,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,QAAQ,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,WAAW;YAAE,MAAM;QAC9E,MAAM,GAAG,QAAQ,CAAC,UAAU,CAAC,WAAW,CAAC;IAC3C,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Document360 environment presets — baked defaults, overridable at runtime via
3
+ * D360_* env vars (decided in plans/first-party-d360-integration.md: no CI-variable
4
+ * injection; client_id etc. are PKCE public-client values, not secrets).
5
+ */
6
+ import type { ProfileConnection } from '../config.js';
7
+ export type D360Environment = {
8
+ /** Preset this connection is based on (display/label only; auth is keyed by PROFILE name). */
9
+ name: string;
10
+ apiUrl: string;
11
+ authorizationUrl: string;
12
+ tokenUrl: string;
13
+ clientId: string;
14
+ scopes: string[];
15
+ acrValues: string;
16
+ redirectUri: string;
17
+ /** OAuth prompt param. 'login' forces a fresh auth screen, avoiding stale-session/cached
18
+ error pages in the default browser (no incognito needed). Empty string omits it. */
19
+ prompt: string;
20
+ };
21
+ export declare function knownEnvironments(): string[];
22
+ export declare function resolveEnvironment(name?: string): D360Environment;
23
+ /**
24
+ * Resolve a profile's connection into a concrete D360Environment. Layering, lowest→highest:
25
+ * baked preset (named by `connection.environment`, default berlin) → inline profile fields →
26
+ * D360_* env vars (testing escape hatch).
27
+ */
28
+ export declare function resolveConnection(connection: ProfileConnection): D360Environment;
@@ -0,0 +1,54 @@
1
+ const PRESETS = {
2
+ berlin: {
3
+ apiUrl: 'https://apihub.berlin.document360.net',
4
+ authorizationUrl: 'https://identity.berlin.document360.net/connect/authorize',
5
+ tokenUrl: 'https://identity.berlin.document360.net/connect/token',
6
+ // Dedicated d360-writer client, confirmed working for Customer API V3 OAuth
7
+ // on berlin (D360 API team, 2026-06-08).
8
+ clientId: 'd360WriterAgentClient',
9
+ // offline_access → refresh token (added to the client by the D360 API team 2026-06-08,
10
+ // resolving the 1h-session limitation).
11
+ scopes: ['openid', 'profile', 'email', 'customerApi', 'offline_access'],
12
+ // Identity server shows the project chooser during login; token is project-scoped.
13
+ acrValues: 'project_select',
14
+ // RFC 8252 loopback — whitelisted for d360WriterAgentClient (D360 API team,
15
+ // 2026-06-08). Port 3223 is fixed because the redirect URI must match the
16
+ // registered value exactly. Override with D360_REDIRECT_URI only for testing.
17
+ redirectUri: 'http://127.0.0.1:3223/callback',
18
+ // Force a fresh login screen so a stale browser session/cached error page can't
19
+ // hijack the flow (was forcing users into incognito). Override with D360_PROMPT.
20
+ prompt: 'login',
21
+ },
22
+ // production: filled at Document360 OAuth GA (end of June 2026).
23
+ };
24
+ export function knownEnvironments() {
25
+ return Object.keys(PRESETS);
26
+ }
27
+ export function resolveEnvironment(name = 'berlin') {
28
+ return resolveConnection({ environment: name });
29
+ }
30
+ /**
31
+ * Resolve a profile's connection into a concrete D360Environment. Layering, lowest→highest:
32
+ * baked preset (named by `connection.environment`, default berlin) → inline profile fields →
33
+ * D360_* env vars (testing escape hatch).
34
+ */
35
+ export function resolveConnection(connection) {
36
+ const presetName = connection.environment ?? 'berlin';
37
+ const preset = PRESETS[presetName];
38
+ if (!preset) {
39
+ throw new Error(`Unknown Document360 environment "${presetName}". Known: ${knownEnvironments().join(', ')}`);
40
+ }
41
+ const envScopes = process.env.D360_SCOPES?.split(/[\s,]+/).filter(Boolean);
42
+ return {
43
+ name: presetName,
44
+ apiUrl: process.env.D360_API_URL ?? connection.apiUrl ?? preset.apiUrl,
45
+ authorizationUrl: process.env.D360_AUTHORIZATION_URL ?? connection.authorizationUrl ?? preset.authorizationUrl,
46
+ tokenUrl: process.env.D360_TOKEN_URL ?? connection.tokenUrl ?? preset.tokenUrl,
47
+ clientId: process.env.D360_CLIENT_ID ?? connection.clientId ?? preset.clientId,
48
+ scopes: envScopes ?? connection.scopes ?? preset.scopes,
49
+ acrValues: process.env.D360_ACR_VALUES ?? connection.acrValues ?? preset.acrValues,
50
+ redirectUri: process.env.D360_REDIRECT_URI ?? connection.redirectUri ?? preset.redirectUri,
51
+ prompt: process.env.D360_PROMPT ?? connection.prompt ?? preset.prompt,
52
+ };
53
+ }
54
+ //# sourceMappingURL=environments.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"environments.js","sourceRoot":"","sources":["../../src/d360/environments.ts"],"names":[],"mappings":"AAsBA,MAAM,OAAO,GAAkD;IAC7D,MAAM,EAAE;QACN,MAAM,EAAE,uCAAuC;QAC/C,gBAAgB,EAAE,2DAA2D;QAC7E,QAAQ,EAAE,uDAAuD;QACjE,4EAA4E;QAC5E,yCAAyC;QACzC,QAAQ,EAAE,uBAAuB;QACjC,uFAAuF;QACvF,wCAAwC;QACxC,MAAM,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,aAAa,EAAE,gBAAgB,CAAC;QACvE,mFAAmF;QACnF,SAAS,EAAE,gBAAgB;QAC3B,4EAA4E;QAC5E,0EAA0E;QAC1E,8EAA8E;QAC9E,WAAW,EAAE,gCAAgC;QAC7C,gFAAgF;QAChF,iFAAiF;QACjF,MAAM,EAAE,OAAO;KAChB;IACD,iEAAiE;CAClE,CAAC;AAEF,MAAM,UAAU,iBAAiB;IAC/B,OAAO,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AAC9B,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,IAAI,GAAG,QAAQ;IAChD,OAAO,iBAAiB,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;AAClD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,UAA6B;IAC7D,MAAM,UAAU,GAAG,UAAU,CAAC,WAAW,IAAI,QAAQ,CAAC;IACtD,MAAM,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IACnC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,oCAAoC,UAAU,aAAa,iBAAiB,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC/G,CAAC;IACD,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC3E,OAAO;QACL,IAAI,EAAE,UAAU;QAChB,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,UAAU,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM;QACtE,gBAAgB,EAAE,OAAO,CAAC,GAAG,CAAC,sBAAsB,IAAI,UAAU,CAAC,gBAAgB,IAAI,MAAM,CAAC,gBAAgB;QAC9G,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,UAAU,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ;QAC9E,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,UAAU,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ;QAC9E,MAAM,EAAE,SAAS,IAAI,UAAU,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM;QACvD,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,UAAU,CAAC,SAAS,IAAI,MAAM,CAAC,SAAS;QAClF,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,UAAU,CAAC,WAAW,IAAI,MAAM,CAAC,WAAW;QAC1F,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,UAAU,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM;KACtE,CAAC;AACJ,CAAC"}
@@ -0,0 +1,27 @@
1
+ import type { D360Environment } from './environments.js';
2
+ import type { StoredTokens } from './tokenStore.js';
3
+ export type TokenResponse = {
4
+ access_token: string;
5
+ token_type: string;
6
+ expires_in: number;
7
+ refresh_token?: string;
8
+ id_token?: string;
9
+ scope?: string;
10
+ };
11
+ export declare function buildAuthorizationUrl(env: D360Environment): {
12
+ url: string;
13
+ verifier: string;
14
+ state: string;
15
+ };
16
+ export declare function exchangeCode(env: D360Environment, code: string, verifier: string): Promise<TokenResponse>;
17
+ export declare function refreshTokens(env: D360Environment, refreshToken: string): Promise<TokenResponse>;
18
+ export declare function toStoredTokens(profile: string, t: TokenResponse): StoredTokens;
19
+ /**
20
+ * Interactive login. Default: loopback listener + browser. Manual mode: no
21
+ * listener — the user pastes the full redirect URL (works even when the
22
+ * registered redirect URI is not a loopback we can bind).
23
+ */
24
+ export declare function loginPkce(env: D360Environment, opts: {
25
+ manual?: boolean;
26
+ promptForRedirect: (msg: string) => Promise<string>;
27
+ }, log: (line: string) => void): Promise<TokenResponse>;