melchizedek-agents 1.0.3 → 1.2.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/.env.example +29 -2
- package/AGENT_SETUP.md +3 -1
- package/CHANGELOG.md +524 -0
- package/DOCUMENTATION.md +178 -43
- package/ONBOARDING.md +339 -0
- package/QUICKSTART.md +15 -2
- package/README.md +6 -2
- package/config/agents/README.md +2 -2
- package/config/agents/examples/ares.yaml +6 -10
- package/config/agents/examples/assistant.yaml +6 -6
- package/config/agents/examples/augustin.yaml +9 -15
- package/config/agents/examples/cartographers.yaml +6 -6
- package/config/agents/examples/council.yaml +7 -7
- package/config/agents/examples/critic.yaml +24 -24
- package/config/agents/examples/harness.yaml +4 -4
- package/config/agents/examples/image_production.yaml +37 -35
- package/config/agents/examples/librarian.yaml +4 -4
- package/config/agents/examples/patient_advocate.yaml +6 -10
- package/config/agents/examples/pipeline.yaml +20 -19
- package/config/agents/examples/research.yaml +36 -43
- package/config/agents/examples/scribe.yaml +31 -32
- package/config/agents/examples/scriptorium.yaml +6 -6
- package/config/agents/examples/structured_critic.yaml +82 -0
- package/config/agents/examples/style_council.yaml +9 -11
- package/config/agents/examples/syndicate.yaml +3 -5
- package/config/agents/examples/tutor.yaml +2 -2
- package/config/agents/examples/weather.yaml +2 -2
- package/config/agents/syndicate.schema.json +859 -13
- package/config/agents/syndicateSchema.yaml +122 -73
- package/config/agents/templates/README.md +1 -1
- package/config/agents/templates/account_memory.yaml +4 -4
- package/config/agents/templates/case_desk.yaml +6 -6
- package/config/agents/templates/conversational.yaml +2 -2
- package/config/agents/templates/draft_review.yaml +6 -6
- package/config/agents/templates/intake_extractor.yaml +64 -62
- package/config/agents/templates/research_brief.yaml +6 -6
- package/config/agents/templates/research_desk.yaml +6 -6
- package/config/agents/templates/review_panel.yaml +8 -8
- package/config/agents/templates/support_triage.yaml +30 -29
- package/config/agents/templates/systems_operator.yaml +24 -5
- package/db/migrations/0014_durable_runs.sql +31 -0
- package/dist/lib/a2a/app.d.ts +27 -0
- package/dist/lib/a2a/app.js +64 -5
- package/dist/lib/a2a/executor.d.ts +7 -1
- package/dist/lib/a2a/executor.js +43 -5
- package/dist/lib/a2a/oauthSetup.d.ts +106 -0
- package/dist/lib/a2a/oauthSetup.js +259 -0
- package/dist/lib/agentDialect.d.ts +76 -0
- package/dist/lib/agentDialect.js +151 -0
- package/dist/lib/compile.d.ts +42 -6
- package/dist/lib/compile.js +97 -45
- package/dist/lib/compileNative.d.ts +11 -3
- package/dist/lib/compileNative.js +20 -1
- package/dist/lib/dispatch.d.ts +10 -5
- package/dist/lib/dispatch.js +10 -5
- package/dist/lib/doctor.d.ts +110 -1
- package/dist/lib/doctor.js +205 -9
- package/dist/lib/loadSyndicate.d.ts +54 -2
- package/dist/lib/loadSyndicate.js +22 -2
- package/dist/lib/memory/namespace.d.ts +6 -5
- package/dist/lib/memory/namespace.js +6 -5
- package/dist/lib/memory/providers.js +3 -4
- package/dist/lib/models/capabilities.d.ts +25 -1
- package/dist/lib/models/capabilities.js +79 -1
- package/dist/lib/models/chatCompletionsAdapter.d.ts +11 -3
- package/dist/lib/models/chatCompletionsAdapter.js +19 -5
- package/dist/lib/models/claudeAdapter.js +4 -0
- package/dist/lib/models/contract.d.ts +5 -2
- package/dist/lib/models/gateway.d.ts +2 -1
- package/dist/lib/models/gateway.js +4 -2
- package/dist/lib/models/gatewayAdapter.d.ts +7 -1
- package/dist/lib/models/gatewayAdapter.js +8 -0
- package/dist/lib/models/geminiAdapter.js +8 -2
- package/dist/lib/models/genaiMapping.d.ts +8 -4
- package/dist/lib/models/genaiMapping.js +23 -9
- package/dist/lib/models/gptAdapter.d.ts +4 -2
- package/dist/lib/models/gptAdapter.js +11 -4
- package/dist/lib/models/grokAdapter.d.ts +3 -1
- package/dist/lib/models/grokAdapter.js +11 -3
- package/dist/lib/models/kimiAdapter.d.ts +2 -1
- package/dist/lib/models/kimiAdapter.js +3 -3
- package/dist/lib/models/reasoning.d.ts +30 -2
- package/dist/lib/models/reasoning.js +56 -5
- package/dist/lib/observability/tracer.d.ts +8 -1
- package/dist/lib/observability/tracer.js +33 -5
- package/dist/lib/onboarding.d.ts +134 -0
- package/dist/lib/onboarding.js +539 -0
- package/dist/lib/runtime/approvals.d.ts +8 -0
- package/dist/lib/runtime/approvals.js +4 -3
- package/dist/lib/runtime/credentials.d.ts +6 -0
- package/dist/lib/runtime/native/agentLoop.d.ts +23 -3
- package/dist/lib/runtime/native/agentLoop.js +107 -11
- package/dist/lib/runtime/native/checkpoint.d.ts +171 -0
- package/dist/lib/runtime/native/checkpoint.js +359 -0
- package/dist/lib/runtime/native/delegate.d.ts +222 -18
- package/dist/lib/runtime/native/delegate.js +390 -39
- package/dist/lib/runtime/native/interrupts.d.ts +184 -2
- package/dist/lib/runtime/native/interrupts.js +391 -1
- package/dist/lib/runtime/native/request.d.ts +13 -3
- package/dist/lib/runtime/native/request.js +28 -4
- package/dist/lib/runtime/native/telemetry.js +10 -15
- package/dist/lib/runtime/questions.d.ts +7 -4
- package/dist/lib/runtime/sessions.d.ts +1 -1
- package/dist/lib/runtime/syndicateTurn.d.ts +11 -3
- package/dist/lib/runtime/syndicateTurn.js +353 -68
- package/dist/lib/runtime/turnControl.d.ts +56 -0
- package/dist/lib/runtime/turnControl.js +10 -0
- package/dist/lib/session/transcript.d.ts +9 -0
- package/dist/lib/session/transcript.js +20 -1
- package/dist/lib/skills.d.ts +18 -0
- package/dist/lib/skills.js +73 -1
- package/dist/lib/storage/postgres/index.d.ts +12 -2
- package/dist/lib/storage/postgres/index.js +8 -3
- package/dist/lib/storage/postgres/taskQueue.d.ts +20 -4
- package/dist/lib/storage/postgres/taskQueue.js +29 -5
- package/dist/lib/storage/postgres/taskStore.d.ts +26 -2
- package/dist/lib/storage/postgres/taskStore.js +74 -8
- package/dist/lib/syndicateSchema.d.ts +316 -1
- package/dist/lib/syndicateSchema.js +203 -29
- package/dist/lib/tools/auth.d.ts +20 -6
- package/dist/lib/tools/auth.js +21 -5
- package/dist/lib/tools/credentialEnv.d.ts +28 -0
- package/dist/lib/tools/credentialEnv.js +52 -0
- package/dist/lib/tools/credentialHosts.d.ts +88 -0
- package/dist/lib/tools/credentialHosts.js +165 -0
- package/dist/lib/tools/credentialStore.js +9 -2
- package/dist/lib/tools/credentialUses.d.ts +92 -0
- package/dist/lib/tools/credentialUses.js +112 -0
- package/dist/lib/tools/mcpToolFactory.d.ts +25 -2
- package/dist/lib/tools/mcpToolFactory.js +179 -21
- package/dist/lib/tools/oauthConsent.js +8 -1
- package/dist/lib/tools/oauthHosts.d.ts +73 -0
- package/dist/lib/tools/oauthHosts.js +154 -0
- package/dist/lib/tools/oauthTools.d.ts +242 -0
- package/dist/lib/tools/oauthTools.js +447 -0
- package/dist/lib/tools/openapiTools.d.ts +11 -6
- package/dist/lib/tools/openapiTools.js +88 -38
- package/dist/lib/tools/taskTools.d.ts +61 -6
- package/dist/lib/tools/taskTools.js +145 -21
- package/dist/lib/workflow/turn.d.ts +22 -3
- package/dist/lib/workflow/turn.js +151 -31
- package/dist/lib/workflow.d.ts +14 -7
- package/dist/lib/workflow.js +14 -7
- package/dist/lib/workflowConfig.d.ts +6 -0
- package/dist/scripts/a2a_server.d.ts +36 -0
- package/dist/scripts/a2a_server.js +131 -1
- package/dist/scripts/assistant_worker.d.ts +12 -0
- package/dist/scripts/assistant_worker.js +81 -22
- package/dist/scripts/doctor.d.ts +7 -2
- package/dist/scripts/doctor.js +9 -4
- package/dist/scripts/setup.d.ts +23 -0
- package/dist/scripts/setup.js +154 -0
- package/dist/scripts/skills_install.d.ts +3 -1
- package/dist/scripts/skills_install.js +17 -2
- package/dist/scripts/yaml_codemod.d.ts +62 -0
- package/dist/scripts/yaml_codemod.js +652 -0
- package/package.json +8 -2
- package/skills/README.md +11 -2
- package/skills/briefs/_shared.md +1 -1
- package/skills/briefs/melchizedek-author.md +7 -5
- package/skills/briefs/melchizedek-models.md +7 -4
- package/skills/briefs/melchizedek.md +1 -0
- package/skills/briefs/skills-readme.md +3 -1
- package/skills/melchizedek/SKILL.md +1 -0
- package/skills/melchizedek-author/SKILL.md +7 -4
- package/skills/melchizedek-author/assets/minimal.yaml +13 -13
- package/skills/melchizedek-models/SKILL.md +11 -4
- package/skills/melchizedek-onboard/SKILL.md +49 -0
- package/skills/melchizedek-onboard-cloud/SKILL.md +38 -0
- package/skills/melchizedek-onboard-keys/SKILL.md +35 -0
- package/skills/melchizedek-onboard-local/SKILL.md +41 -0
- package/skills/melchizedek-onboard-serve/SKILL.md +43 -0
package/dist/lib/workflow.js
CHANGED
|
@@ -41,16 +41,23 @@
|
|
|
41
41
|
* A delegated `yaml_reference` to a workflow syndicate is the whole graph,
|
|
42
42
|
* walked under the subagent entry's name and description on a child
|
|
43
43
|
* session (lib/compileNative.ts workflowSubagentOf); its answer is the
|
|
44
|
-
* graph's last event's text. A
|
|
45
|
-
*
|
|
46
|
-
*
|
|
44
|
+
* graph's last event's text. A walk that pauses (an `ask_user` node, a
|
|
45
|
+
* gated agent node) leaves the call open, and the turn ends input-required
|
|
46
|
+
* with the agent path down to the node; the answer walks the graph again
|
|
47
|
+
* (lib/runtime/native/delegate.ts resumeWorkflowSubagent, ADR 0111).
|
|
47
48
|
*
|
|
48
49
|
* ── As a dispatch route or a workflow node (ADR 0106) ────────────────────
|
|
49
50
|
* A `yaml_reference` to a workflow syndicate runs its whole graph there too,
|
|
50
|
-
* on the child session filed under the
|
|
51
|
-
* route's
|
|
52
|
-
*
|
|
53
|
-
* (lib/
|
|
51
|
+
* on the child session filed under the agent path and its kind
|
|
52
|
+
* (`<app>/route:<entry>`, `<walk's app>/node:<entry>`, ADR 0119, ADR 0120),
|
|
53
|
+
* as a subagent does. A route's answer is what the graph would
|
|
54
|
+
* answer as its own syndicate (lib/runtime/syndicateTurn.ts); a node's is
|
|
55
|
+
* the nested walk's (lib/workflow/turn.ts). A map over one is refused
|
|
56
|
+
* (lib/compile.ts). Its pauses (an `ask_user` node, a gate) reach the turn
|
|
57
|
+
* with the path from the entry down to the node that asked: a route's
|
|
58
|
+
* through the route's pause record in the conversation, a node's through
|
|
59
|
+
* its own pause on the caller's walk; the answer walks the nested graph
|
|
60
|
+
* again (ADR 0119).
|
|
54
61
|
*
|
|
55
62
|
* ── Approval gates (ADR 0098, ADR 0106) ──────────────────────────────────
|
|
56
63
|
* A tool in a node agent's `require_approval`, and a skill script
|
|
@@ -80,6 +80,12 @@ export interface PendingInput {
|
|
|
80
80
|
payload?: unknown;
|
|
81
81
|
/** JSON Schema a structured answer must satisfy, when the node declared one. */
|
|
82
82
|
schema?: unknown;
|
|
83
|
+
/**
|
|
84
|
+
* For a question asked inside a delegated subagent (ADR 0110): the agents
|
|
85
|
+
* from the turn's own agent down to the one that asked, `node` last.
|
|
86
|
+
* Absent when the turn's own agent (or a workflow node) asked.
|
|
87
|
+
*/
|
|
88
|
+
path?: string[];
|
|
83
89
|
}
|
|
84
90
|
/** ADK's function-call name for a workflow input request. */
|
|
85
91
|
export declare const INPUT_REQUEST = "adk_request_input";
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import type { Server } from 'node:http';
|
|
3
3
|
import { createA2AApp } from '../lib/a2a/app.ts';
|
|
4
|
+
import type { SyndicateYamlConfig } from '../lib/loadSyndicate.ts';
|
|
5
|
+
import type { OAuthServerSetup } from '../lib/a2a/oauthSetup.ts';
|
|
6
|
+
import type { AuditSink } from '../lib/observability/audit.ts';
|
|
7
|
+
import type { CredentialRows } from '../lib/tools/credentialStore.ts';
|
|
4
8
|
export { describeTurnError } from '../lib/a2a/executor.ts';
|
|
5
9
|
/**
|
|
6
10
|
* What a deployment adds on top of the environment-driven server: its own
|
|
@@ -15,4 +19,36 @@ export interface ServerExtensions {
|
|
|
15
19
|
/** Any other createA2AApp option; wins over what the environment sets. */
|
|
16
20
|
options?: Partial<Parameters<typeof createA2AApp>[0]>;
|
|
17
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* The syndicates this server serves from FILES: the default (unless it is a
|
|
24
|
+
* registry row), each served id that is not a registry id, and, when every
|
|
25
|
+
* file is served (A2A_SERVED_AGENTS unset or *), every syndicate at the
|
|
26
|
+
* agents directory's root. The consent step's OAuth clients come from these
|
|
27
|
+
* only (ADR 0114): a registry row may name a provider they declare, never
|
|
28
|
+
* define one. A file that does not load is skipped with a warning (its route
|
|
29
|
+
* fails on its own).
|
|
30
|
+
*/
|
|
31
|
+
export declare function servedFileSyndicates(syndicateName: string, servedAgents: string[] | undefined, registryAgents: string[] | undefined, warn?: (m: string) => void): SyndicateYamlConfig[];
|
|
32
|
+
/** The server's tool credentials, from the environment: what createA2AApp takes, and what to print. */
|
|
33
|
+
export interface ServerOAuth extends OAuthServerSetup {
|
|
34
|
+
toolCredentials?: OAuthServerSetup['toolCredentials'] & {
|
|
35
|
+
requireCallerIdentity?: boolean;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Tool credentials and the consent step for the served syndicates, from
|
|
40
|
+
* MELCHIZEDEK_CREDENTIAL_KEY, OAUTH_REDIRECT_URI, OAUTH_CALLBACK_IDENTITY,
|
|
41
|
+
* MELCHIZEDEK_OAUTH_HOSTS and MELCHIZEDEK_CREDENTIAL_HOSTS (lib/a2a/oauthSetup.ts). Throws, naming
|
|
42
|
+
* variables only, on a configuration that is unsafe or cannot work.
|
|
43
|
+
*/
|
|
44
|
+
export declare function serverOAuth(options: {
|
|
45
|
+
syndicateName: string;
|
|
46
|
+
servedAgents?: string[];
|
|
47
|
+
registryAgents?: string[];
|
|
48
|
+
rows?: CredentialRows;
|
|
49
|
+
audit?: AuditSink;
|
|
50
|
+
env?: NodeJS.ProcessEnv;
|
|
51
|
+
fetch?: typeof fetch;
|
|
52
|
+
warn?: (m: string) => void;
|
|
53
|
+
}): ServerOAuth;
|
|
18
54
|
export declare function startServer(syndicateName?: string, extensions?: ServerExtensions): Promise<Server>;
|
|
@@ -75,9 +75,29 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
|
|
|
75
75
|
* running turn (default 20). Behind a session-mode
|
|
76
76
|
* pooler (Supabase :5432) keep the two sums under its
|
|
77
77
|
* client limit, per instance.
|
|
78
|
+
* MELCHIZEDEK_CREDENTIAL_KEY 32 random bytes (base64 or hex) that seal each user's
|
|
79
|
+
* third-party OAuth tokens (ADR 0072). Unset = no tool
|
|
80
|
+
* credentials. Rows live in Postgres with DATABASE_URL,
|
|
81
|
+
* else in process memory
|
|
82
|
+
* OAUTH_REDIRECT_URI the consent callback, exactly as registered at every
|
|
83
|
+
* provider (https://agents.example.com/oauth/callback);
|
|
84
|
+
* mounts the callback and lets a run ask a user to
|
|
85
|
+
* connect (ADR 0085). Needs MELCHIZEDEK_CREDENTIAL_KEY
|
|
86
|
+
* OAUTH_CALLBACK_IDENTITY required (default): the callback's browser must carry the
|
|
87
|
+
* flow's user's identity (A2A_AUTH=header behind a gateway);
|
|
88
|
+
* state: the single-use state nonce alone binds it (ADR 0114)
|
|
89
|
+
* MELCHIZEDEK_OAUTH_HOSTS provider=host,host;…: the hosts each provider's tokens may
|
|
90
|
+
* be sent to. Unset: authorization_code grants are refused,
|
|
91
|
+
* client_credentials allowed (ADR 0114)
|
|
92
|
+
* MELCHIZEDEK_CREDENTIAL_HOSTS VARIABLE=host,host;…: the hosts each static credential
|
|
93
|
+
* variable a YAML sends (bearer_env, api_key.env,
|
|
94
|
+
* client_secret_env) may be sent to. Set, it is the whole
|
|
95
|
+
* list. Unset: sent where the YAML says, and the boot names
|
|
96
|
+
* each unbound variable (ADR 0122)
|
|
78
97
|
*/
|
|
79
98
|
import { randomBytes } from 'node:crypto';
|
|
80
|
-
import { realpathSync, writeFileSync } from 'node:fs';
|
|
99
|
+
import { readdirSync, realpathSync, writeFileSync } from 'node:fs';
|
|
100
|
+
import path from 'node:path';
|
|
81
101
|
import { pathToFileURL } from 'node:url';
|
|
82
102
|
import { createA2AApp } from '../lib/a2a/app.js';
|
|
83
103
|
import { callerTokens, firstOf, hashCallerToken, jwtIdentity, parseCallers, sharedSecret, trustedHeader, } from '../lib/a2a/identity.js';
|
|
@@ -90,6 +110,11 @@ import { postgresStorage } from '../lib/storage/postgres/index.js';
|
|
|
90
110
|
import { isPlaceholderValue, loadEnv } from '../lib/loadEnv.js';
|
|
91
111
|
import { flushTracing } from '../lib/observability/tracer.js';
|
|
92
112
|
import { setLogLevel } from '../lib/runtime/logging.js';
|
|
113
|
+
import { loadSyndicate } from '../lib/loadSyndicate.js';
|
|
114
|
+
import { callbackIdentity, callbackIdentityProblem, oauthServerSetup } from '../lib/a2a/oauthSetup.js';
|
|
115
|
+
import { postgresCredentialRows } from '../lib/storage/postgres/credentialStore.js';
|
|
116
|
+
import { setOAuthHosts } from '../lib/tools/oauthHosts.js';
|
|
117
|
+
import { setCredentialHosts } from '../lib/tools/credentialHosts.js';
|
|
93
118
|
// Re-exported for existing importers (tests/failNarration.test.ts).
|
|
94
119
|
export { describeTurnError } from '../lib/a2a/executor.js';
|
|
95
120
|
/** Minimum bearer-secret length below which the server warns. */
|
|
@@ -132,6 +157,87 @@ function fatal(message) {
|
|
|
132
157
|
emit('error', `[A2A] ✗ FATAL: ${message}`);
|
|
133
158
|
process.exit(1);
|
|
134
159
|
}
|
|
160
|
+
/**
|
|
161
|
+
* The syndicates this server serves from FILES: the default (unless it is a
|
|
162
|
+
* registry row), each served id that is not a registry id, and, when every
|
|
163
|
+
* file is served (A2A_SERVED_AGENTS unset or *), every syndicate at the
|
|
164
|
+
* agents directory's root. The consent step's OAuth clients come from these
|
|
165
|
+
* only (ADR 0114): a registry row may name a provider they declare, never
|
|
166
|
+
* define one. A file that does not load is skipped with a warning (its route
|
|
167
|
+
* fails on its own).
|
|
168
|
+
*/
|
|
169
|
+
export function servedFileSyndicates(syndicateName, servedAgents, registryAgents, warn = () => { }) {
|
|
170
|
+
const registry = new Set(registryAgents ?? []);
|
|
171
|
+
const files = new Set();
|
|
172
|
+
const add = (id) => {
|
|
173
|
+
if (id.startsWith('registry:') || registry.has(id))
|
|
174
|
+
return;
|
|
175
|
+
files.add(id.endsWith('.yaml') || id.endsWith('.yml') ? id : `${id}.yaml`);
|
|
176
|
+
};
|
|
177
|
+
add(syndicateName);
|
|
178
|
+
if (servedAgents) {
|
|
179
|
+
for (const id of servedAgents)
|
|
180
|
+
add(id);
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
const agentsDir = path.resolve(process.env.MELCHIZEDEK_AGENTS_DIR ?? path.join(process.cwd(), 'config', 'agents'));
|
|
184
|
+
let names = [];
|
|
185
|
+
try {
|
|
186
|
+
names = readdirSync(agentsDir);
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
names = [];
|
|
190
|
+
}
|
|
191
|
+
for (const f of names.sort())
|
|
192
|
+
if ((f.endsWith('.yaml') || f.endsWith('.yml')) && f !== 'syndicateSchema.yaml')
|
|
193
|
+
files.add(f);
|
|
194
|
+
}
|
|
195
|
+
const out = [];
|
|
196
|
+
for (const file of files) {
|
|
197
|
+
try {
|
|
198
|
+
out.push(loadSyndicate(file, { shippedFallback: servedAgents !== undefined || file === syndicateName }));
|
|
199
|
+
}
|
|
200
|
+
catch (err) {
|
|
201
|
+
if (err?.code !== 'ENOENT')
|
|
202
|
+
warn(`OAuth setup skipped ${file}: ${err instanceof Error ? err.message.split('\n')[0] : String(err)}`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return out;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Tool credentials and the consent step for the served syndicates, from
|
|
209
|
+
* MELCHIZEDEK_CREDENTIAL_KEY, OAUTH_REDIRECT_URI, OAUTH_CALLBACK_IDENTITY,
|
|
210
|
+
* MELCHIZEDEK_OAUTH_HOSTS and MELCHIZEDEK_CREDENTIAL_HOSTS (lib/a2a/oauthSetup.ts). Throws, naming
|
|
211
|
+
* variables only, on a configuration that is unsafe or cannot work.
|
|
212
|
+
*/
|
|
213
|
+
export function serverOAuth(options) {
|
|
214
|
+
const env = options.env ?? process.env;
|
|
215
|
+
const identity = callbackIdentity(env);
|
|
216
|
+
const setup = oauthServerSetup({
|
|
217
|
+
configs: servedFileSyndicates(options.syndicateName, options.servedAgents, options.registryAgents, options.warn),
|
|
218
|
+
load: (ref) => loadSyndicate(ref),
|
|
219
|
+
env,
|
|
220
|
+
...(options.rows ? { rows: options.rows } : {}),
|
|
221
|
+
...(options.audit ? { audit: options.audit } : {}),
|
|
222
|
+
allowPrivate: env.ALLOW_PRIVATE_OPENAPI === 'true' || env.ALLOW_PRIVATE_MCP === 'true',
|
|
223
|
+
...(options.fetch ? { fetch: options.fetch } : {}),
|
|
224
|
+
});
|
|
225
|
+
if (!setup.toolCredentials?.consent)
|
|
226
|
+
return setup;
|
|
227
|
+
const warnings = [...setup.warnings];
|
|
228
|
+
const refused = callbackIdentityProblem(env);
|
|
229
|
+
if (identity === 'state') {
|
|
230
|
+
warnings.push('OAUTH_CALLBACK_IDENTITY=state: the single-use state alone binds a consent callback to its user, so a forwarded authorization link can connect the forwarder\'s account to the user who was asked.');
|
|
231
|
+
}
|
|
232
|
+
else if (refused) {
|
|
233
|
+
warnings.push(`${refused.charAt(0).toUpperCase()}${refused.slice(1)}.`);
|
|
234
|
+
}
|
|
235
|
+
return {
|
|
236
|
+
...setup,
|
|
237
|
+
warnings,
|
|
238
|
+
toolCredentials: { ...setup.toolCredentials, requireCallerIdentity: identity === 'required' },
|
|
239
|
+
};
|
|
240
|
+
}
|
|
135
241
|
export async function startServer(syndicateName = 'syndicate.yaml', extensions = {}) {
|
|
136
242
|
loadEnv(import.meta.url);
|
|
137
243
|
// Conversation content stays out of stdout unless the operator opts in:
|
|
@@ -319,6 +425,27 @@ export async function startServer(syndicateName = 'syndicate.yaml', extensions =
|
|
|
319
425
|
if (metricsToken && (isPlaceholderValue(metricsToken) || metricsToken.length < 24)) {
|
|
320
426
|
fatal('A2A_METRICS_TOKEN must be a long random value (openssl rand -hex 32).');
|
|
321
427
|
}
|
|
428
|
+
// ── Tool credentials and the OAuth consent step (ADR 0072, 0085, 0114) ────
|
|
429
|
+
let oauth;
|
|
430
|
+
try {
|
|
431
|
+
// An extension's allowlist wins over the environment's here too, as createA2AApp applies it.
|
|
432
|
+
if (extensions.options?.oauthHosts)
|
|
433
|
+
setOAuthHosts(extensions.options.oauthHosts);
|
|
434
|
+
if (extensions.options?.credentialHosts)
|
|
435
|
+
setCredentialHosts(extensions.options.credentialHosts);
|
|
436
|
+
oauth = serverOAuth({
|
|
437
|
+
syndicateName,
|
|
438
|
+
servedAgents,
|
|
439
|
+
registryAgents,
|
|
440
|
+
...(pgStorage ? { rows: postgresCredentialRows(pgStorage.pool), audit: pgStorage.audit } : {}),
|
|
441
|
+
warn: (m) => emit('warn', `[A2A] ⚠ ${m}`),
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
catch (err) {
|
|
445
|
+
fatal(err instanceof Error ? err.message : String(err));
|
|
446
|
+
}
|
|
447
|
+
for (const w of oauth.warnings)
|
|
448
|
+
emit('warn', `[A2A] ⚠ ${w}`);
|
|
322
449
|
let built;
|
|
323
450
|
try {
|
|
324
451
|
built = await createA2AApp({
|
|
@@ -345,6 +472,7 @@ export async function startServer(syndicateName = 'syndicate.yaml', extensions =
|
|
|
345
472
|
keyMode,
|
|
346
473
|
...(pgStorage ? { storage: pgStorage } : {}),
|
|
347
474
|
...(policy ? { policy } : {}),
|
|
475
|
+
...(oauth.toolCredentials ? { toolCredentials: oauth.toolCredentials } : {}),
|
|
348
476
|
metricsToken,
|
|
349
477
|
log: (m) => emit('info', `[A2A] ${m}`),
|
|
350
478
|
warn: (m) => emit('warn', `[A2A] ⚠ ${m}`),
|
|
@@ -388,6 +516,8 @@ export async function startServer(syndicateName = 'syndicate.yaml', extensions =
|
|
|
388
516
|
}
|
|
389
517
|
if (budgetLabel)
|
|
390
518
|
emit('info', `[A2A] budgets ${budgetLabel}`);
|
|
519
|
+
emit('info', `[A2A] oauth ${oauth.summary}`);
|
|
520
|
+
emit('info', `[A2A] creds ${oauth.credentialSummary}`);
|
|
391
521
|
if (metricsToken)
|
|
392
522
|
emit('info', `[A2A] metrics ${base}/metrics (bearer A2A_METRICS_TOKEN)`);
|
|
393
523
|
emit('info', `[A2A] health ${base}/healthz ${base}/readyz`);
|
|
@@ -21,6 +21,16 @@
|
|
|
21
21
|
* instead of abandoning it while it keeps spending. Any syndicate and any
|
|
22
22
|
* subagent in it can serve as the worker.
|
|
23
23
|
*
|
|
24
|
+
* A run is durable (ADR 0113): runDurableTurn (lib/runtime/native/
|
|
25
|
+
* checkpoint.ts) checkpoints the run's sessions at every step boundary
|
|
26
|
+
* beside the job (TaskBackend.saveCheckpoint), and a job claimed again
|
|
27
|
+
* after its worker stopped resumes from its last checkpoint instead of
|
|
28
|
+
* starting over. SIGTERM aborts the step in flight and puts the job back
|
|
29
|
+
* in the queue with its checkpoint; the next worker finishes it. A job
|
|
30
|
+
* cancelled while it runs (task_update) loses its claim: the next lease
|
|
31
|
+
* renewal or checkpoint save sees it, and the run is aborted without
|
|
32
|
+
* writing a result over the cancellation.
|
|
33
|
+
*
|
|
24
34
|
* Usage:
|
|
25
35
|
* npm run assistant:worker poll every 30 s until Ctrl-C
|
|
26
36
|
* npm run assistant:worker -- --once drain the queue, then exit (cron)
|
|
@@ -30,5 +40,7 @@
|
|
|
30
40
|
* --agent names a subagent of the syndicate, or its orchestrator.
|
|
31
41
|
* MELCHIZEDEK_TASKS_FILE moves the store (default: outputs/tasks.json),
|
|
32
42
|
* and must match the chat process's value. Run ONE worker per store.
|
|
43
|
+
* MELCHIZEDEK_CHECKPOINT_MAX_BYTES caps a stored checkpoint (bytes of
|
|
44
|
+
* JSON, default 5 MiB); a larger one is skipped and the previous kept.
|
|
33
45
|
*/
|
|
34
46
|
export {};
|
|
@@ -21,6 +21,16 @@
|
|
|
21
21
|
* instead of abandoning it while it keeps spending. Any syndicate and any
|
|
22
22
|
* subagent in it can serve as the worker.
|
|
23
23
|
*
|
|
24
|
+
* A run is durable (ADR 0113): runDurableTurn (lib/runtime/native/
|
|
25
|
+
* checkpoint.ts) checkpoints the run's sessions at every step boundary
|
|
26
|
+
* beside the job (TaskBackend.saveCheckpoint), and a job claimed again
|
|
27
|
+
* after its worker stopped resumes from its last checkpoint instead of
|
|
28
|
+
* starting over. SIGTERM aborts the step in flight and puts the job back
|
|
29
|
+
* in the queue with its checkpoint; the next worker finishes it. A job
|
|
30
|
+
* cancelled while it runs (task_update) loses its claim: the next lease
|
|
31
|
+
* renewal or checkpoint save sees it, and the run is aborted without
|
|
32
|
+
* writing a result over the cancellation.
|
|
33
|
+
*
|
|
24
34
|
* Usage:
|
|
25
35
|
* npm run assistant:worker poll every 30 s until Ctrl-C
|
|
26
36
|
* npm run assistant:worker -- --once drain the queue, then exit (cron)
|
|
@@ -30,16 +40,17 @@
|
|
|
30
40
|
* --agent names a subagent of the syndicate, or its orchestrator.
|
|
31
41
|
* MELCHIZEDEK_TASKS_FILE moves the store (default: outputs/tasks.json),
|
|
32
42
|
* and must match the chat process's value. Run ONE worker per store.
|
|
43
|
+
* MELCHIZEDEK_CHECKPOINT_MAX_BYTES caps a stored checkpoint (bytes of
|
|
44
|
+
* JSON, default 5 MiB); a larger one is skipped and the previous kept.
|
|
33
45
|
*/
|
|
34
46
|
import { randomUUID } from 'node:crypto';
|
|
35
47
|
import { loadEnv } from '../lib/loadEnv.js';
|
|
36
48
|
import { loadSyndicate } from '../lib/loadSyndicate.js';
|
|
37
|
-
import {
|
|
49
|
+
import { runDurableTurn } from '../lib/runtime/native/checkpoint.js';
|
|
38
50
|
import { runtimeSetting } from '../lib/runtime/runtimeFlag.js';
|
|
39
51
|
import { setLogLevel } from '../lib/runtime/logging.js';
|
|
40
|
-
import { InProcessSessionService } from '../lib/runtime/sessions.js';
|
|
41
52
|
import { PROVIDERS, providerForModel, providerKeyPresent, } from '../lib/models/registry.js';
|
|
42
|
-
import { getTaskBackend, setTaskBackend, taskStorePath } from '../lib/tools/taskTools.js';
|
|
53
|
+
import { CHECKPOINT_MAX_BYTES_ENV, applyInterrupted, checkpointMaxBytesSetting, fileTaskBackendWith, getTaskBackend, setTaskBackend, taskStorePath, } from '../lib/tools/taskTools.js';
|
|
43
54
|
import { postgresStorage } from '../lib/storage/postgres/index.js';
|
|
44
55
|
import { dbSchema } from '../lib/storage/schema.js';
|
|
45
56
|
import { hostname } from 'node:os';
|
|
@@ -81,10 +92,17 @@ if (!providerKeyPresent(provider)) {
|
|
|
81
92
|
const log = (message) => console.log(`[worker] ${message}`);
|
|
82
93
|
// The queue: the JSON file, or Postgres when DATABASE_URL is set, where any
|
|
83
94
|
// number of workers may run (each claim is FOR UPDATE SKIP LOCKED).
|
|
95
|
+
// A checkpoint above MELCHIZEDEK_CHECKPOINT_MAX_BYTES (bytes of JSON, default
|
|
96
|
+
// 5 MiB) is not saved: the job keeps its previous one (ADR 0113).
|
|
97
|
+
const rawCap = process.env[CHECKPOINT_MAX_BYTES_ENV];
|
|
98
|
+
const checkpointMaxBytes = checkpointMaxBytesSetting(rawCap);
|
|
99
|
+
if (rawCap?.trim() && Number(rawCap.trim()) !== checkpointMaxBytes) {
|
|
100
|
+
log(`${CHECKPOINT_MAX_BYTES_ENV} is not a positive whole number of bytes; using ${checkpointMaxBytes}`);
|
|
101
|
+
}
|
|
102
|
+
const taskQueue = { checkpointMaxBytes, log };
|
|
84
103
|
const databaseUrl = process.env.DATABASE_URL?.trim();
|
|
85
|
-
const pg = databaseUrl ? postgresStorage({ connectionString: databaseUrl, schema: dbSchema() }) : undefined;
|
|
86
|
-
|
|
87
|
-
setTaskBackend(pg.taskQueue);
|
|
104
|
+
const pg = databaseUrl ? postgresStorage({ connectionString: databaseUrl, schema: dbSchema(), taskQueue }) : undefined;
|
|
105
|
+
setTaskBackend(pg ? pg.taskQueue : fileTaskBackendWith(taskQueue));
|
|
88
106
|
const backend = getTaskBackend();
|
|
89
107
|
/** A claimed job stays this worker's while it renews; the job timeout bounds a turn. */
|
|
90
108
|
const lease = { workerId: `${hostname()}:${process.pid}:${randomUUID().slice(0, 8)}`, leaseMs: 60_000 };
|
|
@@ -105,24 +123,43 @@ const workerConfig = !sub
|
|
|
105
123
|
};
|
|
106
124
|
/** The job in hand, so a signal can cancel it. */
|
|
107
125
|
let current;
|
|
108
|
-
|
|
126
|
+
/** Set by SIGTERM: the job in hand goes back to the queue with its checkpoint. */
|
|
127
|
+
let interrupted = false;
|
|
128
|
+
/** The run lost its claim on the job (cancelled while it ran, or taken by another worker). */
|
|
129
|
+
class ClaimLost extends Error {
|
|
130
|
+
}
|
|
131
|
+
/** SIGTERM stopped the run; the job is re-queued, not failed. */
|
|
132
|
+
class Interrupted extends Error {
|
|
133
|
+
}
|
|
134
|
+
// ── One job = one durable single-turn run (ADR 0113) ────────────────────────
|
|
109
135
|
async function runJob(job) {
|
|
110
|
-
|
|
136
|
+
const controller = new AbortController();
|
|
137
|
+
current = controller;
|
|
111
138
|
try {
|
|
112
|
-
|
|
139
|
+
// A fresh in-process store per attempt, restored from the job's last
|
|
140
|
+
// checkpoint when an earlier attempt left one (ADR 0080, ADR 0113).
|
|
141
|
+
const result = await runDurableTurn({
|
|
113
142
|
config: workerConfig,
|
|
114
143
|
parts: [{ text: job.instruction ?? job.title }],
|
|
115
144
|
appName: 'assistant-worker',
|
|
116
145
|
userId: 'local-user',
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
146
|
+
runId: `${job.owner}:${job.id}`,
|
|
147
|
+
checkpoints: {
|
|
148
|
+
load: async () => ((await backend.loadCheckpoint?.(job)) ?? null),
|
|
149
|
+
save: async (checkpoint) => (backend.saveCheckpoint ? backend.saveCheckpoint(lease, job, checkpoint) : true),
|
|
150
|
+
},
|
|
151
|
+
onSaveError: (e) => log(`checkpoint not saved: ${e instanceof Error ? e.message : String(e)}`),
|
|
121
152
|
compile: { log, onUnknownTool: (n) => log(`unknown tool '${n}' skipped`) },
|
|
122
|
-
signal:
|
|
153
|
+
signal: controller.signal,
|
|
123
154
|
deadlineMs: JOB_TIMEOUT_MS,
|
|
124
155
|
events: { warn: (m) => log(`⚠ ${m}`) },
|
|
125
156
|
});
|
|
157
|
+
if (result.resumedFromStep > 0)
|
|
158
|
+
log(`${job.id} resumed from step ${result.resumedFromStep}`);
|
|
159
|
+
if (result.lostClaim || controller.signal.reason instanceof ClaimLost)
|
|
160
|
+
throw new ClaimLost('the job was cancelled or claimed elsewhere');
|
|
161
|
+
if (interrupted)
|
|
162
|
+
throw new Interrupted('stopped by SIGTERM');
|
|
126
163
|
if (result.status !== 'completed') {
|
|
127
164
|
throw new Error(`${result.error?.code ?? 'ERROR'}: ${result.error?.message ?? ''}`.trim());
|
|
128
165
|
}
|
|
@@ -142,10 +179,17 @@ async function drain() {
|
|
|
142
179
|
const started = Date.now();
|
|
143
180
|
const label = job.owner ? `${job.id} (${job.owner})` : job.id;
|
|
144
181
|
log(`${label} started: ${job.title}`);
|
|
145
|
-
// Renew the lease while the job runs, so no other worker takes it back
|
|
182
|
+
// Renew the lease while the job runs, so no other worker takes it back;
|
|
183
|
+
// a renewal that finds the claim gone (the job was cancelled) stops the run.
|
|
146
184
|
const held = job;
|
|
147
185
|
const heartbeat = setInterval(() => {
|
|
148
|
-
backend
|
|
186
|
+
backend
|
|
187
|
+
.renew(lease, held)
|
|
188
|
+
.then((kept) => {
|
|
189
|
+
if (kept === false)
|
|
190
|
+
current?.abort(new ClaimLost('claim lost'));
|
|
191
|
+
})
|
|
192
|
+
.catch((e) => log(`lease renewal failed: ${e instanceof Error ? e.message : e}`));
|
|
149
193
|
}, Math.floor(lease.leaseMs / 3));
|
|
150
194
|
try {
|
|
151
195
|
const result = await runJob(job);
|
|
@@ -153,8 +197,22 @@ async function drain() {
|
|
|
153
197
|
log(`${label} done in ${Math.round((Date.now() - started) / 1000)} s`);
|
|
154
198
|
}
|
|
155
199
|
catch (error) {
|
|
156
|
-
|
|
157
|
-
|
|
200
|
+
if (error instanceof ClaimLost) {
|
|
201
|
+
// Cancelled while it ran: the record already says so; nothing is written over it.
|
|
202
|
+
log(`${label} stopped: ${error.message}`);
|
|
203
|
+
}
|
|
204
|
+
else if (error instanceof Interrupted) {
|
|
205
|
+
// Back to the queue with its checkpoint, or failed after MAX_ATTEMPTS interruptions.
|
|
206
|
+
const outcome = await backend.mutate(job.owner, (store) => {
|
|
207
|
+
const t = store.tasks.find((r) => r.id === held.id);
|
|
208
|
+
return t && t.status === 'running' ? applyInterrupted(t) : 'left as is';
|
|
209
|
+
});
|
|
210
|
+
log(`${label} interrupted: ${outcome}`);
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
await backend.finish(job, { error: String(error?.message ?? error) });
|
|
214
|
+
log(`${label} failed: ${error?.message ?? error}`);
|
|
215
|
+
}
|
|
158
216
|
}
|
|
159
217
|
finally {
|
|
160
218
|
clearInterval(heartbeat);
|
|
@@ -183,12 +241,13 @@ process.on('SIGINT', () => {
|
|
|
183
241
|
stopping = true;
|
|
184
242
|
log('stopping after the current job (Ctrl-C again to quit now)');
|
|
185
243
|
});
|
|
186
|
-
// A container stop:
|
|
187
|
-
//
|
|
244
|
+
// A container stop: abort the step in flight and put the job back in the
|
|
245
|
+
// queue with its checkpoint, so the next worker resumes it (ADR 0113).
|
|
188
246
|
process.on('SIGTERM', () => {
|
|
189
247
|
stopping = true;
|
|
190
|
-
|
|
191
|
-
current
|
|
248
|
+
interrupted = true;
|
|
249
|
+
log('SIGTERM — stopping the current job; it resumes from its last checkpoint');
|
|
250
|
+
current?.abort(new Interrupted('SIGTERM'));
|
|
192
251
|
});
|
|
193
252
|
while (!stopping) {
|
|
194
253
|
await drain();
|
package/dist/scripts/doctor.d.ts
CHANGED
|
@@ -8,7 +8,12 @@
|
|
|
8
8
|
* gateway stand-in, or local), which declared server-side tools it keeps or
|
|
9
9
|
* drops, and one verdict per syndicate. Says first which runtime a turn runs
|
|
10
10
|
* on (native; a leftover MELCHIZEDEK_RUNTIME=adk is a problem, since 1.0.0
|
|
11
|
-
* removed that runtime).
|
|
11
|
+
* removed that runtime). Lists the tools that need an OAuth grant (an
|
|
12
|
+
* `auth: { oauth2 }` or `mcp_auth`, ADR 0112): each user's consent, or the
|
|
13
|
+
* server's own client credentials and whether their variables are set
|
|
14
|
+
* (names only). When any A2A identity or billing variable is set, a
|
|
15
|
+
* `serving` line names the A2A_AUTH mode and A2A_KEY_MODE and what the
|
|
16
|
+
* server would refuse to start on. Closes with the env vars that would
|
|
12
17
|
* unlock the most, where to get each, and the first command to try.
|
|
13
18
|
*
|
|
14
19
|
* Read-only: never edits .env, never sends a request, never prints a key
|
|
@@ -17,7 +22,7 @@
|
|
|
17
22
|
* Usage:
|
|
18
23
|
* npm run doctor
|
|
19
24
|
* npm run doctor -- --json # machine-readable
|
|
20
|
-
* npm run doctor -- --check # exit 1
|
|
25
|
+
* npm run doctor -- --check # exit 1 on a blocked syndicate, a runtime problem, or an OAuth or credential host problem
|
|
21
26
|
* npm run doctor -- --matrix # the provider × capability matrix
|
|
22
27
|
* npm run doctor -- --fix-namespaces [file…] # give long-term syndicates a memory_namespace
|
|
23
28
|
* MELCHIZEDEK_AGENTS_DIR=/path npm run doctor
|
package/dist/scripts/doctor.js
CHANGED
|
@@ -8,7 +8,12 @@
|
|
|
8
8
|
* gateway stand-in, or local), which declared server-side tools it keeps or
|
|
9
9
|
* drops, and one verdict per syndicate. Says first which runtime a turn runs
|
|
10
10
|
* on (native; a leftover MELCHIZEDEK_RUNTIME=adk is a problem, since 1.0.0
|
|
11
|
-
* removed that runtime).
|
|
11
|
+
* removed that runtime). Lists the tools that need an OAuth grant (an
|
|
12
|
+
* `auth: { oauth2 }` or `mcp_auth`, ADR 0112): each user's consent, or the
|
|
13
|
+
* server's own client credentials and whether their variables are set
|
|
14
|
+
* (names only). When any A2A identity or billing variable is set, a
|
|
15
|
+
* `serving` line names the A2A_AUTH mode and A2A_KEY_MODE and what the
|
|
16
|
+
* server would refuse to start on. Closes with the env vars that would
|
|
12
17
|
* unlock the most, where to get each, and the first command to try.
|
|
13
18
|
*
|
|
14
19
|
* Read-only: never edits .env, never sends a request, never prints a key
|
|
@@ -17,7 +22,7 @@
|
|
|
17
22
|
* Usage:
|
|
18
23
|
* npm run doctor
|
|
19
24
|
* npm run doctor -- --json # machine-readable
|
|
20
|
-
* npm run doctor -- --check # exit 1
|
|
25
|
+
* npm run doctor -- --check # exit 1 on a blocked syndicate, a runtime problem, or an OAuth or credential host problem
|
|
21
26
|
* npm run doctor -- --matrix # the provider × capability matrix
|
|
22
27
|
* npm run doctor -- --fix-namespaces [file…] # give long-term syndicates a memory_namespace
|
|
23
28
|
* MELCHIZEDEK_AGENTS_DIR=/path npm run doctor
|
|
@@ -25,7 +30,7 @@
|
|
|
25
30
|
import fs from 'node:fs';
|
|
26
31
|
import path from 'node:path';
|
|
27
32
|
import { loadEnv } from '../lib/loadEnv.js';
|
|
28
|
-
import { renderDoctor, runDoctor } from '../lib/doctor.js';
|
|
33
|
+
import { checkProblems, renderDoctor, runDoctor } from '../lib/doctor.js';
|
|
29
34
|
import { renderCapabilityMatrix } from '../lib/models/capabilities.js';
|
|
30
35
|
import { assignMemoryNamespace, LEGACY_MEMORY_APP_NAME } from '../lib/memory/namespace.js';
|
|
31
36
|
loadEnv();
|
|
@@ -88,5 +93,5 @@ if (json) {
|
|
|
88
93
|
else {
|
|
89
94
|
console.log(renderDoctor(result, { color: !noColor }));
|
|
90
95
|
}
|
|
91
|
-
if (check && (result.
|
|
96
|
+
if (check && checkProblems(result).length)
|
|
92
97
|
process.exit(1);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* scripts/setup.ts — the `melchizedek-setup` bin and `npm run setup`.
|
|
4
|
+
*
|
|
5
|
+
* The onboarding command: a menu of authentication levels (no key, one
|
|
6
|
+
* provider, several, a gateway, a cloud platform, per-caller keys, caller
|
|
7
|
+
* tokens, OAuth tool grants, and an honest entry for subscription sign-ins),
|
|
8
|
+
* each with a startup guide: the variables to set (names and shapes only),
|
|
9
|
+
* the doctor command that confirms it, the shipped files that run there and
|
|
10
|
+
* the first commands to try. The levels and guides live in lib/onboarding.ts;
|
|
11
|
+
* ONBOARDING.md is generated from the same function.
|
|
12
|
+
*
|
|
13
|
+
* npx melchizedek-setup # the menu (interactive terminal)
|
|
14
|
+
* npx melchizedek-setup --auto # detect from the environment, names only
|
|
15
|
+
* npx melchizedek-setup --level gateway # one guide, by id or number
|
|
16
|
+
* npx melchizedek-setup --level 2 --write-env # also create .env from .env.example
|
|
17
|
+
* npx melchizedek-setup --list
|
|
18
|
+
* npx melchizedek-setup --markdown # ONBOARDING.md, regenerated
|
|
19
|
+
*
|
|
20
|
+
* Never prints a value. `--write-env` never overwrites `.env`, leaves every
|
|
21
|
+
* name blank, and refuses unless `git check-ignore` confirms `.env` is ignored.
|
|
22
|
+
*/
|
|
23
|
+
export declare function main(argv?: string[]): Promise<number>;
|