@opensip-cli/config 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.
- package/LICENSE +202 -0
- package/NOTICE +8 -0
- package/README.md +31 -0
- package/dist/capability-preferences.d.ts +39 -0
- package/dist/capability-preferences.d.ts.map +1 -0
- package/dist/capability-preferences.js +61 -0
- package/dist/capability-preferences.js.map +1 -0
- package/dist/composer.d.ts +58 -0
- package/dist/composer.d.ts.map +1 -0
- package/dist/composer.js +111 -0
- package/dist/composer.js.map +1 -0
- package/dist/declaration.d.ts +59 -0
- package/dist/declaration.d.ts.map +1 -0
- package/dist/declaration.js +15 -0
- package/dist/declaration.js.map +1 -0
- package/dist/document/cli-config.d.ts +102 -0
- package/dist/document/cli-config.d.ts.map +1 -0
- package/dist/document/cli-config.js +150 -0
- package/dist/document/cli-config.js.map +1 -0
- package/dist/document/dashboard.d.ts +21 -0
- package/dist/document/dashboard.d.ts.map +1 -0
- package/dist/document/dashboard.js +21 -0
- package/dist/document/dashboard.js.map +1 -0
- package/dist/document/global-config.d.ts +101 -0
- package/dist/document/global-config.d.ts.map +1 -0
- package/dist/document/global-config.js +163 -0
- package/dist/document/global-config.js.map +1 -0
- package/dist/document/host-declarations.d.ts +44 -0
- package/dist/document/host-declarations.d.ts.map +1 -0
- package/dist/document/host-declarations.js +48 -0
- package/dist/document/host-declarations.js.map +1 -0
- package/dist/document/targeting.d.ts +151 -0
- package/dist/document/targeting.d.ts.map +1 -0
- package/dist/document/targeting.js +71 -0
- package/dist/document/targeting.js.map +1 -0
- package/dist/document/template.d.ts +43 -0
- package/dist/document/template.d.ts.map +1 -0
- package/dist/document/template.js +63 -0
- package/dist/document/template.js.map +1 -0
- package/dist/index.d.ts +39 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/dist/json-schema.d.ts +23 -0
- package/dist/json-schema.d.ts.map +1 -0
- package/dist/json-schema.js +25 -0
- package/dist/json-schema.js.map +1 -0
- package/dist/namespace-claims.d.ts +29 -0
- package/dist/namespace-claims.d.ts.map +1 -0
- package/dist/namespace-claims.js +62 -0
- package/dist/namespace-claims.js.map +1 -0
- package/dist/precedence.d.ts +47 -0
- package/dist/precedence.d.ts.map +1 -0
- package/dist/precedence.js +103 -0
- package/dist/precedence.js.map +1 -0
- package/package.json +47 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cli-config — the tool-agnostic `cli:` block of `opensip-cli.config.yml`.
|
|
3
|
+
*
|
|
4
|
+
* Owns three things, all relocated here under ADR-0023 §Amendment:
|
|
5
|
+
* - {@link CliDefaults} — the structural type the CLI pre-action hook reads.
|
|
6
|
+
* - {@link loadCliDefaults} — the permissive loader the hook calls to merge
|
|
7
|
+
* project-wide defaults (`--report-to`, `--exclude`, `--json`, `--api-key`,
|
|
8
|
+
* `verbose`, `debug`, the `cli.cloud` sync block) into Commander opts.
|
|
9
|
+
* - {@link cliConfigSchema} — the Zod schema the host registers as the `cli`
|
|
10
|
+
* document-level declaration so the composed whole-document validation
|
|
11
|
+
* STRICT-rejects a typo in `cli:` before dispatch.
|
|
12
|
+
*
|
|
13
|
+
* Previously this lived in `@opensip-cli/contracts` (`cli-config.ts`) — a
|
|
14
|
+
* runtime YAML projection in a types-only package (the standing charter
|
|
15
|
+
* violation ADR-0023 names). It now lives in the config layer, beside the rest
|
|
16
|
+
* of the document blocks. The generic `readYamlFile` / `resolveProjectConfigPath`
|
|
17
|
+
* primitives stay in `core` (path/read primitives — the config-resolution
|
|
18
|
+
* decision in the ADR amendment); this module imports them from core.
|
|
19
|
+
*
|
|
20
|
+
* The loader is deliberately permissive — missing config, malformed YAML, or an
|
|
21
|
+
* absent `cli:` key all return `{}`. Strict validation is the composed
|
|
22
|
+
* document's job (the `cli` host declaration), not this reader's.
|
|
23
|
+
*/
|
|
24
|
+
import { z } from 'zod';
|
|
25
|
+
/**
|
|
26
|
+
* Shape of the `cli:` block in `opensip-cli.config.yml` as the CLI pre-action
|
|
27
|
+
* hook reads it. The structural mirror of {@link cliConfigSchema}.
|
|
28
|
+
*/
|
|
29
|
+
export interface CliDefaults {
|
|
30
|
+
readonly exclude?: readonly string[];
|
|
31
|
+
readonly verbose?: boolean;
|
|
32
|
+
readonly json?: boolean;
|
|
33
|
+
readonly reportTo?: string;
|
|
34
|
+
readonly apiKey?: string;
|
|
35
|
+
readonly fileTypes?: readonly string[];
|
|
36
|
+
readonly ignore?: readonly string[];
|
|
37
|
+
readonly debug?: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Presentation settings (the `cli.ui:` sub-block). Currently just the
|
|
40
|
+
* banner size shown above each command. Unlike the other defaults this
|
|
41
|
+
* does NOT map onto a Commander flag — there is no `--banner`; it rides
|
|
42
|
+
* on `RunScope.ui` and is read by the render paths directly.
|
|
43
|
+
*/
|
|
44
|
+
readonly ui?: {
|
|
45
|
+
/** Banner art: `mini` (default) | `lg` | `md` | `sm`. */
|
|
46
|
+
readonly banner?: 'lg' | 'md' | 'sm' | 'mini';
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* OpenSIP Cloud signal sync (ADR-0008). When the customer has an API key
|
|
50
|
+
* and is entitled to the storage tier, each run additionally emits its
|
|
51
|
+
* signals to OpenSIP Cloud (best-effort; local SQLite is unaffected).
|
|
52
|
+
* `sync` defaults to `true` when entitled — set `false` to opt out.
|
|
53
|
+
* `endpoint` overrides the built-in OpenSIP Cloud URL (must be https).
|
|
54
|
+
*/
|
|
55
|
+
readonly cloud?: {
|
|
56
|
+
readonly sync?: boolean;
|
|
57
|
+
readonly endpoint?: string;
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* The Zod schema for the `cli:` document-level block. A superset of the legacy
|
|
62
|
+
* fitness `CliDefaultsSchema` (it additionally claims `debug` and the
|
|
63
|
+
* `cli.cloud` sub-block the permissive loader always read) so the composed
|
|
64
|
+
* STRICT validation never rejects a key the loader honours. Strictness is
|
|
65
|
+
* applied at the document level by the composer (`.strict()` on the namespace),
|
|
66
|
+
* so nested `ui`/`cloud` objects stay lenient — matching prior behaviour.
|
|
67
|
+
*/
|
|
68
|
+
export declare const cliConfigSchema: z.ZodObject<{
|
|
69
|
+
exclude: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
70
|
+
verbose: z.ZodOptional<z.ZodBoolean>;
|
|
71
|
+
json: z.ZodOptional<z.ZodBoolean>;
|
|
72
|
+
reportTo: z.ZodOptional<z.ZodURL>;
|
|
73
|
+
apiKey: z.ZodOptional<z.ZodString>;
|
|
74
|
+
fileTypes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
75
|
+
ignore: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
76
|
+
debug: z.ZodOptional<z.ZodBoolean>;
|
|
77
|
+
ui: z.ZodOptional<z.ZodObject<{
|
|
78
|
+
banner: z.ZodOptional<z.ZodEnum<{
|
|
79
|
+
lg: "lg";
|
|
80
|
+
md: "md";
|
|
81
|
+
sm: "sm";
|
|
82
|
+
mini: "mini";
|
|
83
|
+
}>>;
|
|
84
|
+
}, z.core.$strip>>;
|
|
85
|
+
cloud: z.ZodOptional<z.ZodObject<{
|
|
86
|
+
sync: z.ZodOptional<z.ZodBoolean>;
|
|
87
|
+
endpoint: z.ZodOptional<z.ZodString>;
|
|
88
|
+
}, z.core.$strip>>;
|
|
89
|
+
}, z.core.$strip>;
|
|
90
|
+
/**
|
|
91
|
+
* Best-effort load of the `cli:` block from `opensip-cli.config.yml`.
|
|
92
|
+
* Resolves the file via the core `resolveProjectConfigPath` primitive.
|
|
93
|
+
*
|
|
94
|
+
* Returns `{}` when the config is missing, unreadable, malformed, or has no
|
|
95
|
+
* `cli:` section — the merge step treats absence and "everything default" the
|
|
96
|
+
* same.
|
|
97
|
+
*
|
|
98
|
+
* @param cwd Project root for config resolution.
|
|
99
|
+
* @param explicitPath Optional `--config <path>` override.
|
|
100
|
+
*/
|
|
101
|
+
export declare function loadCliDefaults(cwd: string, explicitPath?: string): CliDefaults;
|
|
102
|
+
//# sourceMappingURL=cli-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-config.d.ts","sourceRoot":"","sources":["../../src/document/cli-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,CAAC,EAAE;QACZ,yDAAyD;QACzD,QAAQ,CAAC,MAAM,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,MAAM,CAAC;KAC/C,CAAC;IACF;;;;;;OAMG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE;QACf,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;QACxB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;KAC5B,CAAC;CACH;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;iBAoB1B,CAAC;AA6DH;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,WAAW,CAa/E"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cli-config — the tool-agnostic `cli:` block of `opensip-cli.config.yml`.
|
|
3
|
+
*
|
|
4
|
+
* Owns three things, all relocated here under ADR-0023 §Amendment:
|
|
5
|
+
* - {@link CliDefaults} — the structural type the CLI pre-action hook reads.
|
|
6
|
+
* - {@link loadCliDefaults} — the permissive loader the hook calls to merge
|
|
7
|
+
* project-wide defaults (`--report-to`, `--exclude`, `--json`, `--api-key`,
|
|
8
|
+
* `verbose`, `debug`, the `cli.cloud` sync block) into Commander opts.
|
|
9
|
+
* - {@link cliConfigSchema} — the Zod schema the host registers as the `cli`
|
|
10
|
+
* document-level declaration so the composed whole-document validation
|
|
11
|
+
* STRICT-rejects a typo in `cli:` before dispatch.
|
|
12
|
+
*
|
|
13
|
+
* Previously this lived in `@opensip-cli/contracts` (`cli-config.ts`) — a
|
|
14
|
+
* runtime YAML projection in a types-only package (the standing charter
|
|
15
|
+
* violation ADR-0023 names). It now lives in the config layer, beside the rest
|
|
16
|
+
* of the document blocks. The generic `readYamlFile` / `resolveProjectConfigPath`
|
|
17
|
+
* primitives stay in `core` (path/read primitives — the config-resolution
|
|
18
|
+
* decision in the ADR amendment); this module imports them from core.
|
|
19
|
+
*
|
|
20
|
+
* The loader is deliberately permissive — missing config, malformed YAML, or an
|
|
21
|
+
* absent `cli:` key all return `{}`. Strict validation is the composed
|
|
22
|
+
* document's job (the `cli` host declaration), not this reader's.
|
|
23
|
+
*/
|
|
24
|
+
import { readYamlFile, resolveProjectConfigPath } from '@opensip-cli/core';
|
|
25
|
+
import { z } from 'zod';
|
|
26
|
+
/**
|
|
27
|
+
* The Zod schema for the `cli:` document-level block. A superset of the legacy
|
|
28
|
+
* fitness `CliDefaultsSchema` (it additionally claims `debug` and the
|
|
29
|
+
* `cli.cloud` sub-block the permissive loader always read) so the composed
|
|
30
|
+
* STRICT validation never rejects a key the loader honours. Strictness is
|
|
31
|
+
* applied at the document level by the composer (`.strict()` on the namespace),
|
|
32
|
+
* so nested `ui`/`cloud` objects stay lenient — matching prior behaviour.
|
|
33
|
+
*/
|
|
34
|
+
export const cliConfigSchema = z.object({
|
|
35
|
+
exclude: z.array(z.string()).optional(),
|
|
36
|
+
verbose: z.boolean().optional(),
|
|
37
|
+
json: z.boolean().optional(),
|
|
38
|
+
reportTo: z.url().optional(),
|
|
39
|
+
apiKey: z.string().min(1).optional(),
|
|
40
|
+
fileTypes: z.array(z.string()).optional(),
|
|
41
|
+
ignore: z.array(z.string()).optional(),
|
|
42
|
+
debug: z.boolean().optional(),
|
|
43
|
+
ui: z
|
|
44
|
+
.object({
|
|
45
|
+
banner: z.enum(['lg', 'md', 'sm', 'mini']).optional(),
|
|
46
|
+
})
|
|
47
|
+
.optional(),
|
|
48
|
+
cloud: z
|
|
49
|
+
.object({
|
|
50
|
+
sync: z.boolean().optional(),
|
|
51
|
+
endpoint: z.string().optional(),
|
|
52
|
+
})
|
|
53
|
+
.optional(),
|
|
54
|
+
});
|
|
55
|
+
/** Valid `ui.banner` values; anything else is dropped (→ default applies). */
|
|
56
|
+
const BANNER_VALUES = new Set(['lg', 'md', 'sm', 'mini']);
|
|
57
|
+
/**
|
|
58
|
+
* Type guard for permissive YAML reading. We accept anything that
|
|
59
|
+
* looks like a plain object; everything else collapses to `{}`.
|
|
60
|
+
*/
|
|
61
|
+
function isPlainObject(v) {
|
|
62
|
+
return typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
63
|
+
}
|
|
64
|
+
/** Coerce a YAML value into a `string[]` if it is one; otherwise drop it. */
|
|
65
|
+
function asStringArray(value) {
|
|
66
|
+
if (!Array.isArray(value))
|
|
67
|
+
return undefined;
|
|
68
|
+
if (!value.every((v) => typeof v === 'string'))
|
|
69
|
+
return undefined;
|
|
70
|
+
return value;
|
|
71
|
+
}
|
|
72
|
+
/** Project an arbitrary YAML object into the typed `CliDefaults` shape. */
|
|
73
|
+
function projectCliDefaults(raw) {
|
|
74
|
+
const out = {};
|
|
75
|
+
const exclude = asStringArray(raw.exclude);
|
|
76
|
+
if (exclude)
|
|
77
|
+
out.exclude = exclude;
|
|
78
|
+
if (typeof raw.verbose === 'boolean')
|
|
79
|
+
out.verbose = raw.verbose;
|
|
80
|
+
if (typeof raw.json === 'boolean')
|
|
81
|
+
out.json = raw.json;
|
|
82
|
+
if (typeof raw.reportTo === 'string')
|
|
83
|
+
out.reportTo = raw.reportTo;
|
|
84
|
+
if (typeof raw.apiKey === 'string')
|
|
85
|
+
out.apiKey = raw.apiKey;
|
|
86
|
+
const fileTypes = asStringArray(raw.fileTypes);
|
|
87
|
+
if (fileTypes)
|
|
88
|
+
out.fileTypes = fileTypes;
|
|
89
|
+
const ignore = asStringArray(raw.ignore);
|
|
90
|
+
if (ignore)
|
|
91
|
+
out.ignore = ignore;
|
|
92
|
+
if (typeof raw.debug === 'boolean')
|
|
93
|
+
out.debug = raw.debug;
|
|
94
|
+
const ui = projectUiDefaults(raw.ui);
|
|
95
|
+
if (ui)
|
|
96
|
+
out.ui = ui;
|
|
97
|
+
const cloud = projectCloudDefaults(raw.cloud);
|
|
98
|
+
if (cloud)
|
|
99
|
+
out.cloud = cloud;
|
|
100
|
+
return out;
|
|
101
|
+
}
|
|
102
|
+
/** Project the `cli.cloud:` sub-block (sync flag + endpoint override) into the typed shape. */
|
|
103
|
+
function projectCloudDefaults(raw) {
|
|
104
|
+
if (!isPlainObject(raw))
|
|
105
|
+
return undefined;
|
|
106
|
+
const out = {};
|
|
107
|
+
if (typeof raw.sync === 'boolean')
|
|
108
|
+
out.sync = raw.sync;
|
|
109
|
+
if (typeof raw.endpoint === 'string')
|
|
110
|
+
out.endpoint = raw.endpoint;
|
|
111
|
+
return out.sync === undefined && out.endpoint === undefined ? undefined : out;
|
|
112
|
+
}
|
|
113
|
+
/** Project the `cli.ui:` sub-block into the typed shape; drop unknown banner values. */
|
|
114
|
+
function projectUiDefaults(raw) {
|
|
115
|
+
if (!isPlainObject(raw))
|
|
116
|
+
return undefined;
|
|
117
|
+
if (typeof raw.banner === 'string' && BANNER_VALUES.has(raw.banner)) {
|
|
118
|
+
return { banner: raw.banner };
|
|
119
|
+
}
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Best-effort load of the `cli:` block from `opensip-cli.config.yml`.
|
|
124
|
+
* Resolves the file via the core `resolveProjectConfigPath` primitive.
|
|
125
|
+
*
|
|
126
|
+
* Returns `{}` when the config is missing, unreadable, malformed, or has no
|
|
127
|
+
* `cli:` section — the merge step treats absence and "everything default" the
|
|
128
|
+
* same.
|
|
129
|
+
*
|
|
130
|
+
* @param cwd Project root for config resolution.
|
|
131
|
+
* @param explicitPath Optional `--config <path>` override.
|
|
132
|
+
*/
|
|
133
|
+
export function loadCliDefaults(cwd, explicitPath) {
|
|
134
|
+
let filePath;
|
|
135
|
+
try {
|
|
136
|
+
filePath = resolveProjectConfigPath(cwd, explicitPath);
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
// @fitness-ignore-next-line error-handling-quality -- documented contract (see JSDoc above): failure to resolve the project config is equivalent to "no cli: section" and treated identically by the merge step.
|
|
140
|
+
return {};
|
|
141
|
+
}
|
|
142
|
+
const doc = readYamlFile(filePath);
|
|
143
|
+
if (!isPlainObject(doc))
|
|
144
|
+
return {};
|
|
145
|
+
const cliBlock = doc.cli;
|
|
146
|
+
if (!isPlainObject(cliBlock))
|
|
147
|
+
return {};
|
|
148
|
+
return projectCliDefaults(cliBlock);
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=cli-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-config.js","sourceRoot":"","sources":["../../src/document/cli-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,YAAY,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAC3E,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAsCxB;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;IACtC,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;IACvC,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IAC/B,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IAC5B,QAAQ,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IAC5B,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IACpC,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;IACzC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;IACtC,KAAK,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IAC7B,EAAE,EAAE,CAAC;SACF,MAAM,CAAC;QACN,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,QAAQ,EAAE;KACtD,CAAC;SACD,QAAQ,EAAE;IACb,KAAK,EAAE,CAAC;SACL,MAAM,CAAC;QACN,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;QAC5B,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;KAChC,CAAC;SACD,QAAQ,EAAE;CACd,CAAC,CAAC;AAEH,8EAA8E;AAC9E,MAAM,aAAa,GAAwB,IAAI,GAAG,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;AAE/E;;;GAGG;AACH,SAAS,aAAa,CAAC,CAAU;IAC/B,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AAClE,CAAC;AAED,6EAA6E;AAC7E,SAAS,aAAa,CAAC,KAAc;IACnC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAC5C,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC;QAAE,OAAO,SAAS,CAAC;IACjE,OAAO,KAAK,CAAC;AACf,CAAC;AAED,2EAA2E;AAC3E,SAAS,kBAAkB,CAAC,GAA4B;IACtD,MAAM,GAAG,GAA2D,EAAE,CAAC;IACvE,MAAM,OAAO,GAAG,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAC3C,IAAI,OAAO;QAAE,GAAG,CAAC,OAAO,GAAG,OAAO,CAAC;IACnC,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,SAAS;QAAE,GAAG,CAAC,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC;IAChE,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,SAAS;QAAE,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;IACvD,IAAI,OAAO,GAAG,CAAC,QAAQ,KAAK,QAAQ;QAAE,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC;IAClE,IAAI,OAAO,GAAG,CAAC,MAAM,KAAK,QAAQ;QAAE,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;IAC5D,MAAM,SAAS,GAAG,aAAa,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAC/C,IAAI,SAAS;QAAE,GAAG,CAAC,SAAS,GAAG,SAAS,CAAC;IACzC,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACzC,IAAI,MAAM;QAAE,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC;IAChC,IAAI,OAAO,GAAG,CAAC,KAAK,KAAK,SAAS;QAAE,GAAG,CAAC,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC;IAC1D,MAAM,EAAE,GAAG,iBAAiB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACrC,IAAI,EAAE;QAAE,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC;IACpB,MAAM,KAAK,GAAG,oBAAoB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAC9C,IAAI,KAAK;QAAE,GAAG,CAAC,KAAK,GAAG,KAAK,CAAC;IAC7B,OAAO,GAAG,CAAC;AACb,CAAC;AAED,+FAA+F;AAC/F,SAAS,oBAAoB,CAAC,GAAY;IACxC,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IAC1C,MAAM,GAAG,GAEL,EAAE,CAAC;IACP,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,SAAS;QAAE,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;IACvD,IAAI,OAAO,GAAG,CAAC,QAAQ,KAAK,QAAQ;QAAE,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC;IAClE,OAAO,GAAG,CAAC,IAAI,KAAK,SAAS,IAAI,GAAG,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC;AAChF,CAAC;AAED,wFAAwF;AACxF,SAAS,iBAAiB,CAAC,GAAY;IACrC,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IAC1C,IAAI,OAAO,GAAG,CAAC,MAAM,KAAK,QAAQ,IAAI,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QACpE,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,MAAkD,EAAE,CAAC;IAC5E,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,eAAe,CAAC,GAAW,EAAE,YAAqB;IAChE,IAAI,QAAgB,CAAC;IACrB,IAAI,CAAC;QACH,QAAQ,GAAG,wBAAwB,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;IACzD,CAAC;IAAC,MAAM,CAAC;QACP,iNAAiN;QACjN,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,MAAM,GAAG,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;IACnC,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,CAAC;IACnC,MAAM,QAAQ,GAAG,GAAG,CAAC,GAAG,CAAC;IACzB,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC;QAAE,OAAO,EAAE,CAAC;IACxC,OAAO,kBAAkB,CAAC,QAAQ,CAAC,CAAC;AACtC,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dashboard — the `dashboard:` document-level block of
|
|
3
|
+
* `opensip-cli.config.yml`.
|
|
4
|
+
*
|
|
5
|
+
* Currently just the editor protocol used by the Code Paths panel to build
|
|
6
|
+
* `vscode://` / `cursor://` deep links. `dashboard` is a CLI-owned
|
|
7
|
+
* composition-root command, not a Tool plugin (ADR-0023 §Amendment), so its
|
|
8
|
+
* config is a host document-level block — structurally identical to `cli`,
|
|
9
|
+
* registered beside it in {@link ./host-declarations}.
|
|
10
|
+
*
|
|
11
|
+
* Relocated here from fitness's `SignalersConfigSchema`. Fitness
|
|
12
|
+
* still reads `dashboard.editor` via `loadSignalersConfig` until it is repointed
|
|
13
|
+
* to the composed scope config (Phase 4); it imports this schema rather than
|
|
14
|
+
* re-defining it, so there is one definition of the block.
|
|
15
|
+
*/
|
|
16
|
+
import { z } from 'zod';
|
|
17
|
+
/** The Zod schema for the `dashboard:` document-level block. */
|
|
18
|
+
export declare const dashboardConfigSchema: z.ZodObject<{
|
|
19
|
+
editor: z.ZodOptional<z.ZodString>;
|
|
20
|
+
}, z.core.$strip>;
|
|
21
|
+
//# sourceMappingURL=dashboard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dashboard.d.ts","sourceRoot":"","sources":["../../src/document/dashboard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,gEAAgE;AAChE,eAAO,MAAM,qBAAqB;;iBAEhC,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dashboard — the `dashboard:` document-level block of
|
|
3
|
+
* `opensip-cli.config.yml`.
|
|
4
|
+
*
|
|
5
|
+
* Currently just the editor protocol used by the Code Paths panel to build
|
|
6
|
+
* `vscode://` / `cursor://` deep links. `dashboard` is a CLI-owned
|
|
7
|
+
* composition-root command, not a Tool plugin (ADR-0023 §Amendment), so its
|
|
8
|
+
* config is a host document-level block — structurally identical to `cli`,
|
|
9
|
+
* registered beside it in {@link ./host-declarations}.
|
|
10
|
+
*
|
|
11
|
+
* Relocated here from fitness's `SignalersConfigSchema`. Fitness
|
|
12
|
+
* still reads `dashboard.editor` via `loadSignalersConfig` until it is repointed
|
|
13
|
+
* to the composed scope config (Phase 4); it imports this schema rather than
|
|
14
|
+
* re-defining it, so there is one definition of the block.
|
|
15
|
+
*/
|
|
16
|
+
import { z } from 'zod';
|
|
17
|
+
/** The Zod schema for the `dashboard:` document-level block. */
|
|
18
|
+
export const dashboardConfigSchema = z.object({
|
|
19
|
+
editor: z.string().min(1).max(64).optional(),
|
|
20
|
+
});
|
|
21
|
+
//# sourceMappingURL=dashboard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../../src/document/dashboard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,gEAAgE;AAChE,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5C,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE;CAC7C,CAAC,CAAC"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* global-config — read/write the user-level (`~/.opensip-cli/config.yml`)
|
|
3
|
+
* config that holds the cloud API key and per-user defaults.
|
|
4
|
+
*
|
|
5
|
+
* User-scoped config I/O is tool-agnostic, so it lives in the config layer
|
|
6
|
+
* (relocated here from the CLI's `bootstrap/`, ADR-0023). The CLI's
|
|
7
|
+
* pre-action hook reads it on every invocation (`mergeConfigDefaults` falls back
|
|
8
|
+
* to the saved API key when neither `--api-key` nor `OPENSIP_API_KEY` is
|
|
9
|
+
* present), and the `configure` command's prompt+UX wrapper — which stays in
|
|
10
|
+
* `cli/commands` — reads/writes I/O through this module.
|
|
11
|
+
*
|
|
12
|
+
* The file is YAML and is `chmod 0o600` on write — it stores a secret.
|
|
13
|
+
* Reads are tolerant of any failure (missing dir, malformed YAML); the
|
|
14
|
+
* pre-action hook treats absence and corruption the same as "no key
|
|
15
|
+
* configured".
|
|
16
|
+
*/
|
|
17
|
+
import { type EnvVarSpec } from '@opensip-cli/core';
|
|
18
|
+
/**
|
|
19
|
+
* Config-layer environment variables (§5.12). Declared as an
|
|
20
|
+
* immutable spec table read through the {@link EnvRegistry} primitive, so the env
|
|
21
|
+
* surface is governed and documentable (the `env-via-registry` guardrail forbids
|
|
22
|
+
* raw `process.env` reads). Re-exported for the generated env-surface doc.
|
|
23
|
+
*/
|
|
24
|
+
export declare const CONFIG_ENV_SPECS: readonly EnvVarSpec<unknown>[];
|
|
25
|
+
/** User-level config file path. */
|
|
26
|
+
export declare const GLOBAL_CONFIG_PATH: string;
|
|
27
|
+
/**
|
|
28
|
+
* Shape of `~/.opensip-cli/config.yml`. Open-ended on purpose — future
|
|
29
|
+
* per-user defaults (theme, last-used recipe, telemetry opt-in) can land
|
|
30
|
+
* here without a contract change.
|
|
31
|
+
*/
|
|
32
|
+
export interface GlobalConfig {
|
|
33
|
+
apiKey?: string;
|
|
34
|
+
/**
|
|
35
|
+
* User-level OpenSIP Cloud signal-sync control (ADR-0008). This is the
|
|
36
|
+
* machine-wide privacy opt-out: `cloud.sync: false` here disables signal
|
|
37
|
+
* sync for every project run from this account, regardless of any project's
|
|
38
|
+
* own `cli.cloud:` setting. `endpoint` overrides the cloud URL per user.
|
|
39
|
+
*/
|
|
40
|
+
cloud?: {
|
|
41
|
+
sync?: boolean;
|
|
42
|
+
endpoint?: string;
|
|
43
|
+
};
|
|
44
|
+
[key: string]: unknown;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Read the user-level global config. Returns `{}` on any failure
|
|
48
|
+
* (missing file, malformed YAML, I/O error) — the merge step treats
|
|
49
|
+
* absence and "everything default" the same.
|
|
50
|
+
*/
|
|
51
|
+
export declare function readGlobalConfig(): GlobalConfig;
|
|
52
|
+
/**
|
|
53
|
+
* Persist the user-level global config. Creates the parent directory if
|
|
54
|
+
* it doesn't exist, then writes via a same-directory temp file with mode
|
|
55
|
+
* `0o600` set at creation time and atomically renames into place.
|
|
56
|
+
*
|
|
57
|
+
* Why temp + rename instead of writeFile + chmod: writeFileSync would
|
|
58
|
+
* create the file using the process's umask (commonly 0o644), leaving a
|
|
59
|
+
* race window during which another local user could read the API key
|
|
60
|
+
* before chmodSync(..., 0o600) tightens permissions. openSync with mode
|
|
61
|
+
* 0o600 + O_EXCL ('wx') sets the permission atomically with the inode
|
|
62
|
+
* creation, and rename publishes the fully-written file in one step so
|
|
63
|
+
* readers never observe a partial file either.
|
|
64
|
+
*/
|
|
65
|
+
export declare function writeGlobalConfig(config: GlobalConfig): void;
|
|
66
|
+
/**
|
|
67
|
+
* Resolve the OpenSIP Cloud API key from the highest-precedence source
|
|
68
|
+
* available. Resolution order:
|
|
69
|
+
*
|
|
70
|
+
* 1. CLI flag (`--api-key`).
|
|
71
|
+
* 2. Environment variable (`OPENSIP_API_KEY`).
|
|
72
|
+
* 3. User-level global config (`~/.opensip-cli/config.yml#apiKey`).
|
|
73
|
+
*
|
|
74
|
+
* The pre-action hook calls this for the global merge step; the
|
|
75
|
+
* `configure` command calls it for the "current key" hint at the
|
|
76
|
+
* prompt.
|
|
77
|
+
*/
|
|
78
|
+
export declare function resolveApiKey(cliFlag?: string): string | undefined;
|
|
79
|
+
/**
|
|
80
|
+
* Resolve the effective cloud config for a run by layering the user-level
|
|
81
|
+
* cloud block (`~/.opensip-cli/config.yml#cloud`) over the project-level
|
|
82
|
+
* `cli.cloud:` block — the missing piece behind audit P0-2, where the
|
|
83
|
+
* documented user opt-out was read for the API key but never for `cloud`.
|
|
84
|
+
*
|
|
85
|
+
* Semantics (a data-egress control, so opt-out is sticky):
|
|
86
|
+
* - `sync` is `false` if EITHER the user (privacy opt-out) or the project
|
|
87
|
+
* (policy opt-out) sets it `false` — the more restrictive wins, neither
|
|
88
|
+
* silently overrides the other. Otherwise the user's explicit value, then
|
|
89
|
+
* the project's.
|
|
90
|
+
* - `endpoint` takes the user override, then the project's.
|
|
91
|
+
* - the per-invocation `--no-cloud` flag overrides everything (applied
|
|
92
|
+
* separately, in resolveSignalSink's `noCloud`).
|
|
93
|
+
*/
|
|
94
|
+
export declare function resolveEffectiveCloudConfig(projectCloud?: {
|
|
95
|
+
readonly sync?: boolean;
|
|
96
|
+
readonly endpoint?: string;
|
|
97
|
+
}): {
|
|
98
|
+
sync?: boolean;
|
|
99
|
+
endpoint?: string;
|
|
100
|
+
} | undefined;
|
|
101
|
+
//# sourceMappingURL=global-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"global-config.d.ts","sourceRoot":"","sources":["../../src/document/global-config.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;GAeG;AAgBH,OAAO,EAAe,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAGjE;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,EAAE,SAAS,UAAU,CAAC,OAAO,CAAC,EAK1D,CAAC;AAKF,mCAAmC;AACnC,eAAO,MAAM,kBAAkB,QAAkC,CAAC;AAElE;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,KAAK,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9C,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,YAAY,CAQ/C;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAsB5D;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAQlE;AAaD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,2BAA2B,CAAC,YAAY,CAAC,EAAE;IACzD,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B,GAAG;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAYpD"}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
// @fitness-ignore-file error-handling-quality -- readGlobalConfig returns {} on any failure by documented contract (absent file and "everything default" are equivalent to the merge step); writeGlobalConfig's inner unlink is cleanup-of-cleanup where the meaningful rename error is already thrown on the next line.
|
|
2
|
+
// @fitness-ignore-file unbounded-memory -- reads ~/.opensip-cli/config.yml, a small user-config file bounded by configuration shape
|
|
3
|
+
/**
|
|
4
|
+
* global-config — read/write the user-level (`~/.opensip-cli/config.yml`)
|
|
5
|
+
* config that holds the cloud API key and per-user defaults.
|
|
6
|
+
*
|
|
7
|
+
* User-scoped config I/O is tool-agnostic, so it lives in the config layer
|
|
8
|
+
* (relocated here from the CLI's `bootstrap/`, ADR-0023). The CLI's
|
|
9
|
+
* pre-action hook reads it on every invocation (`mergeConfigDefaults` falls back
|
|
10
|
+
* to the saved API key when neither `--api-key` nor `OPENSIP_API_KEY` is
|
|
11
|
+
* present), and the `configure` command's prompt+UX wrapper — which stays in
|
|
12
|
+
* `cli/commands` — reads/writes I/O through this module.
|
|
13
|
+
*
|
|
14
|
+
* The file is YAML and is `chmod 0o600` on write — it stores a secret.
|
|
15
|
+
* Reads are tolerant of any failure (missing dir, malformed YAML); the
|
|
16
|
+
* pre-action hook treats absence and corruption the same as "no key
|
|
17
|
+
* configured".
|
|
18
|
+
*/
|
|
19
|
+
import { randomBytes } from 'node:crypto';
|
|
20
|
+
import { closeSync, existsSync, mkdirSync, openSync, readFileSync, renameSync, unlinkSync, writeSync, } from 'node:fs';
|
|
21
|
+
import { homedir } from 'node:os';
|
|
22
|
+
import { join } from 'node:path';
|
|
23
|
+
import { EnvRegistry } from '@opensip-cli/core';
|
|
24
|
+
import { parse as parseYaml, stringify as stringifyYaml } from 'yaml';
|
|
25
|
+
/**
|
|
26
|
+
* Config-layer environment variables (§5.12). Declared as an
|
|
27
|
+
* immutable spec table read through the {@link EnvRegistry} primitive, so the env
|
|
28
|
+
* surface is governed and documentable (the `env-via-registry` guardrail forbids
|
|
29
|
+
* raw `process.env` reads). Re-exported for the generated env-surface doc.
|
|
30
|
+
*/
|
|
31
|
+
export const CONFIG_ENV_SPECS = [
|
|
32
|
+
{
|
|
33
|
+
canonical: 'OPENSIP_API_KEY',
|
|
34
|
+
docs: 'OpenSIP Cloud API key. Overrides the apiKey stored in ~/.opensip-cli/config.yml.',
|
|
35
|
+
},
|
|
36
|
+
];
|
|
37
|
+
const CONFIG_ENV = new EnvRegistry(CONFIG_ENV_SPECS);
|
|
38
|
+
/** User-level OpenSIP root directory. */
|
|
39
|
+
const OPENSIP_DIR = join(homedir(), '.opensip-cli');
|
|
40
|
+
/** User-level config file path. */
|
|
41
|
+
export const GLOBAL_CONFIG_PATH = join(OPENSIP_DIR, 'config.yml');
|
|
42
|
+
/**
|
|
43
|
+
* Read the user-level global config. Returns `{}` on any failure
|
|
44
|
+
* (missing file, malformed YAML, I/O error) — the merge step treats
|
|
45
|
+
* absence and "everything default" the same.
|
|
46
|
+
*/
|
|
47
|
+
export function readGlobalConfig() {
|
|
48
|
+
if (!existsSync(GLOBAL_CONFIG_PATH))
|
|
49
|
+
return {};
|
|
50
|
+
try {
|
|
51
|
+
const raw = readFileSync(GLOBAL_CONFIG_PATH, 'utf8');
|
|
52
|
+
return parseYaml(raw) ?? {};
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return {};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Persist the user-level global config. Creates the parent directory if
|
|
60
|
+
* it doesn't exist, then writes via a same-directory temp file with mode
|
|
61
|
+
* `0o600` set at creation time and atomically renames into place.
|
|
62
|
+
*
|
|
63
|
+
* Why temp + rename instead of writeFile + chmod: writeFileSync would
|
|
64
|
+
* create the file using the process's umask (commonly 0o644), leaving a
|
|
65
|
+
* race window during which another local user could read the API key
|
|
66
|
+
* before chmodSync(..., 0o600) tightens permissions. openSync with mode
|
|
67
|
+
* 0o600 + O_EXCL ('wx') sets the permission atomically with the inode
|
|
68
|
+
* creation, and rename publishes the fully-written file in one step so
|
|
69
|
+
* readers never observe a partial file either.
|
|
70
|
+
*/
|
|
71
|
+
export function writeGlobalConfig(config) {
|
|
72
|
+
if (!existsSync(OPENSIP_DIR)) {
|
|
73
|
+
mkdirSync(OPENSIP_DIR, { recursive: true });
|
|
74
|
+
}
|
|
75
|
+
const tmpPath = join(OPENSIP_DIR, `.config-${randomBytes(6).toString('hex')}.yml.tmp`);
|
|
76
|
+
const fd = openSync(tmpPath, 'wx', 0o600);
|
|
77
|
+
try {
|
|
78
|
+
writeSync(fd, stringifyYaml(config), 0, 'utf8');
|
|
79
|
+
}
|
|
80
|
+
finally {
|
|
81
|
+
closeSync(fd);
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
renameSync(tmpPath, GLOBAL_CONFIG_PATH);
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
// Clean up the temp file on rename failure so it doesn't linger.
|
|
88
|
+
try {
|
|
89
|
+
unlinkSync(tmpPath);
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
// Swallow secondary failure — original error is the one that matters.
|
|
93
|
+
}
|
|
94
|
+
throw error;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Resolve the OpenSIP Cloud API key from the highest-precedence source
|
|
99
|
+
* available. Resolution order:
|
|
100
|
+
*
|
|
101
|
+
* 1. CLI flag (`--api-key`).
|
|
102
|
+
* 2. Environment variable (`OPENSIP_API_KEY`).
|
|
103
|
+
* 3. User-level global config (`~/.opensip-cli/config.yml#apiKey`).
|
|
104
|
+
*
|
|
105
|
+
* The pre-action hook calls this for the global merge step; the
|
|
106
|
+
* `configure` command calls it for the "current key" hint at the
|
|
107
|
+
* prompt.
|
|
108
|
+
*/
|
|
109
|
+
export function resolveApiKey(cliFlag) {
|
|
110
|
+
if (cliFlag)
|
|
111
|
+
return cliFlag;
|
|
112
|
+
// Env override (read through the registry; truthy so an empty value falls
|
|
113
|
+
// through to the config file, byte-identical to the prior `process.env` check).
|
|
114
|
+
const fromEnv = CONFIG_ENV.get('OPENSIP_API_KEY');
|
|
115
|
+
if (fromEnv)
|
|
116
|
+
return fromEnv;
|
|
117
|
+
const config = readGlobalConfig();
|
|
118
|
+
return config.apiKey ?? undefined;
|
|
119
|
+
}
|
|
120
|
+
/** Read + validate the user-level `cloud:` block, defensively. */
|
|
121
|
+
function readUserCloudConfig() {
|
|
122
|
+
const raw = readGlobalConfig().cloud;
|
|
123
|
+
if (!raw || typeof raw !== 'object')
|
|
124
|
+
return undefined;
|
|
125
|
+
const r = raw;
|
|
126
|
+
const out = {};
|
|
127
|
+
if (typeof r.sync === 'boolean')
|
|
128
|
+
out.sync = r.sync;
|
|
129
|
+
if (typeof r.endpoint === 'string')
|
|
130
|
+
out.endpoint = r.endpoint;
|
|
131
|
+
return out.sync === undefined && out.endpoint === undefined ? undefined : out;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Resolve the effective cloud config for a run by layering the user-level
|
|
135
|
+
* cloud block (`~/.opensip-cli/config.yml#cloud`) over the project-level
|
|
136
|
+
* `cli.cloud:` block — the missing piece behind audit P0-2, where the
|
|
137
|
+
* documented user opt-out was read for the API key but never for `cloud`.
|
|
138
|
+
*
|
|
139
|
+
* Semantics (a data-egress control, so opt-out is sticky):
|
|
140
|
+
* - `sync` is `false` if EITHER the user (privacy opt-out) or the project
|
|
141
|
+
* (policy opt-out) sets it `false` — the more restrictive wins, neither
|
|
142
|
+
* silently overrides the other. Otherwise the user's explicit value, then
|
|
143
|
+
* the project's.
|
|
144
|
+
* - `endpoint` takes the user override, then the project's.
|
|
145
|
+
* - the per-invocation `--no-cloud` flag overrides everything (applied
|
|
146
|
+
* separately, in resolveSignalSink's `noCloud`).
|
|
147
|
+
*/
|
|
148
|
+
export function resolveEffectiveCloudConfig(projectCloud) {
|
|
149
|
+
const userCloud = readUserCloudConfig();
|
|
150
|
+
if (!userCloud && !projectCloud)
|
|
151
|
+
return undefined;
|
|
152
|
+
const out = {};
|
|
153
|
+
const sync = userCloud?.sync === false || projectCloud?.sync === false
|
|
154
|
+
? false
|
|
155
|
+
: (userCloud?.sync ?? projectCloud?.sync);
|
|
156
|
+
const endpoint = userCloud?.endpoint ?? projectCloud?.endpoint;
|
|
157
|
+
if (sync !== undefined)
|
|
158
|
+
out.sync = sync;
|
|
159
|
+
if (endpoint !== undefined)
|
|
160
|
+
out.endpoint = endpoint;
|
|
161
|
+
return out.sync === undefined && out.endpoint === undefined ? undefined : out;
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=global-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"global-config.js","sourceRoot":"","sources":["../../src/document/global-config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* host-declarations — the document-level config blocks owned by the host
|
|
3
|
+
* (the config package itself), not by any Tool plugin.
|
|
4
|
+
*
|
|
5
|
+
* Each tool contributes a namespaced {@link ToolConfigDeclaration}
|
|
6
|
+
* (`fitness`/`graph`/`simulation`) that the composer strict-validates. The
|
|
7
|
+
* tool-agnostic blocks — `cli`, `dashboard`, `schemaVersion`, `plugins`, and
|
|
8
|
+
* (from Phase 1) `targets`/`globalExcludes`/`checkOverrides` — are owned by no
|
|
9
|
+
* tool. They are returned here as `ToolConfigDeclaration`s and composed BESIDE
|
|
10
|
+
* the tool declarations (the composer is namespace-agnostic), turning the
|
|
11
|
+
* previously-`.catchall`-tolerated top-level keys into claimed, strict
|
|
12
|
+
* namespaces (ADR-0023).
|
|
13
|
+
*
|
|
14
|
+
* `schemaVersion` is claimed as a permissive top-level `number` so the
|
|
15
|
+
* composed document does not reject it — but the version-COMPAT decision
|
|
16
|
+
* (`readConfigSchemaVersion` + `checkSchemaCompat`) stays in `core`, run in the
|
|
17
|
+
* pre-action gate BEFORE validation (ADR-0023 §Amendment).
|
|
18
|
+
*/
|
|
19
|
+
import { type PluginConfigKeyDeclaration } from './targeting.js';
|
|
20
|
+
import type { ToolConfigDeclaration } from '../declaration.js';
|
|
21
|
+
/**
|
|
22
|
+
* Options for {@link hostConfigDeclarations}.
|
|
23
|
+
*
|
|
24
|
+
* The host's document-level declarations are mostly static, but the `plugins.*`
|
|
25
|
+
* namespace is dynamic — a plugin may contribute its own typed config keys. The
|
|
26
|
+
* composition root collects those and passes them here so the composed schema
|
|
27
|
+
* validates declared plugin keys strictly.
|
|
28
|
+
*/
|
|
29
|
+
export interface HostConfigDeclarationOptions {
|
|
30
|
+
/**
|
|
31
|
+
* Per-plugin config-key declarations discovered for this run. Each becomes a
|
|
32
|
+
* strictly-validated key under the `plugins.<id>` namespace. Omitted (or
|
|
33
|
+
* empty) when no plugin contributes config.
|
|
34
|
+
*/
|
|
35
|
+
readonly pluginConfigKeys?: readonly PluginConfigKeyDeclaration[];
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The host's document-level declarations. Grows in Phase 1 (targeting).
|
|
39
|
+
*
|
|
40
|
+
* Returned as a fresh array per call (no shared mutable state); the
|
|
41
|
+
* composition root concatenates these with the per-tool declarations.
|
|
42
|
+
*/
|
|
43
|
+
export declare function hostConfigDeclarations(options?: HostConfigDeclarationOptions): readonly ToolConfigDeclaration[];
|
|
44
|
+
//# sourceMappingURL=host-declarations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host-declarations.d.ts","sourceRoot":"","sources":["../../src/document/host-declarations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAMH,OAAO,EAKL,KAAK,0BAA0B,EAChC,MAAM,gBAAgB,CAAC;AAExB,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAE/D;;;;;;;GAOG;AACH,MAAM,WAAW,4BAA4B;IAC3C;;;;OAIG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,SAAS,0BAA0B,EAAE,CAAC;CACnE;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,GAAE,4BAAiC,GACzC,SAAS,qBAAqB,EAAE,CAkBlC"}
|