create-absolutejs 0.15.2 → 0.15.4
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/dist/commands/formatProject.js +6 -4
- package/dist/data.js +1 -1
- package/dist/generators/configurations/generatePackageJson.d.ts +2 -2
- package/dist/generators/configurations/generatePackageJson.js +52 -1
- package/dist/generators/db/generateDatabaseTypes.js +1 -1
- package/dist/generators/db/handlerTemplates.d.ts +0 -24
- package/dist/generators/db/handlerTemplates.js +26 -31
- package/dist/generators/db/scaffoldDatabase.js +6 -6
- package/dist/generators/project/generateAbsoluteAuthConfig.d.ts +1 -0
- package/dist/generators/project/generateAbsoluteAuthConfig.js +31 -15
- package/dist/generators/project/generateRoutesBlock.js +3 -2
- package/dist/generators/project/generateServer.js +9 -6
- package/dist/generators/project/scaffoldAgentic.d.ts +7 -0
- package/dist/generators/project/scaffoldAgentic.js +181 -0
- package/dist/generators/project/scaffoldBackend.d.ts +2 -1
- package/dist/generators/project/scaffoldBackend.js +10 -2
- package/dist/generators/project/scaffoldFrontends.js +6 -0
- package/dist/generators/react/generateReactComponents.d.ts +1 -1
- package/dist/generators/react/generateReactComponents.js +7 -3
- package/dist/generators/react/scaffoldReact.d.ts +1 -1
- package/dist/generators/react/scaffoldReact.js +9 -4
- package/dist/messages.d.ts +1 -1
- package/dist/messages.js +3 -1
- package/dist/prompt.js +23 -9
- package/dist/questions/agentic.d.ts +1 -0
- package/dist/questions/agentic.js +11 -0
- package/dist/scaffold.d.ts +1 -1
- package/dist/scaffold.js +7 -2
- package/dist/types.d.ts +4 -0
- package/dist/utils/parseCommandLineOptions.js +14 -5
- package/dist/versions.d.ts +24 -4
- package/dist/versions.js +25 -4
- package/package.json +2 -2
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { mkdirSync, writeFileSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
export const agentRuntimeSource = `import {
|
|
4
|
+
createAgency,
|
|
5
|
+
createAgentControlPlane,
|
|
6
|
+
createMemoryAgencyStore,
|
|
7
|
+
createMemoryAgentControlStore,
|
|
8
|
+
denyAllPolicy
|
|
9
|
+
} from '@absolutejs/agency'
|
|
10
|
+
import {
|
|
11
|
+
createAgentRuntime,
|
|
12
|
+
createMemoryAgentRuntimeStore
|
|
13
|
+
} from '@absolutejs/agent-runtime'
|
|
14
|
+
|
|
15
|
+
// Memory stores are development defaults. Replace them with durable stores
|
|
16
|
+
// before running more than one process or accepting production actions.
|
|
17
|
+
export const agentControl = createAgentControlPlane({
|
|
18
|
+
sources: [],
|
|
19
|
+
store: createMemoryAgentControlStore()
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
// Intentionally deny-by-default. Replace denyAllPolicy() with your policy
|
|
23
|
+
// decision point only after declaring each action's effects and scopes.
|
|
24
|
+
export const agency = createAgency({
|
|
25
|
+
control: agentControl,
|
|
26
|
+
policy: denyAllPolicy(),
|
|
27
|
+
store: createMemoryAgencyStore()
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
// Durable run semantics are available from the first commit. The placeholder
|
|
31
|
+
// driver fails closed until the application supplies its model/tool loop.
|
|
32
|
+
export const agentRuntime = createAgentRuntime({
|
|
33
|
+
store: createMemoryAgentRuntimeStore(),
|
|
34
|
+
driver: {
|
|
35
|
+
next: async () => ({
|
|
36
|
+
type: 'fail',
|
|
37
|
+
code: 'agent_not_configured',
|
|
38
|
+
message: 'Configure the agent driver before accepting runs'
|
|
39
|
+
})
|
|
40
|
+
},
|
|
41
|
+
effects: {
|
|
42
|
+
execute: async () => {
|
|
43
|
+
throw new Error('Agent effects are not configured')
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
`;
|
|
48
|
+
export const agentDiscoverySource = `import {
|
|
49
|
+
ABSOLUTE_AGENT_SCHEMA,
|
|
50
|
+
createAgentDiscoveryHandler,
|
|
51
|
+
signAgentDocument,
|
|
52
|
+
type AgentDiscoveryDocument,
|
|
53
|
+
type DiscoverySigner
|
|
54
|
+
} from '@absolutejs/agent-discovery'
|
|
55
|
+
|
|
56
|
+
// Keep this document specific and keyword-rich: registries rank declared
|
|
57
|
+
// capabilities, effects, scopes, interfaces, examples, and publisher trust.
|
|
58
|
+
export const createAgentDocument = ({
|
|
59
|
+
origin,
|
|
60
|
+
publishedAt,
|
|
61
|
+
version
|
|
62
|
+
}: {
|
|
63
|
+
origin: string
|
|
64
|
+
publishedAt: string
|
|
65
|
+
version: string
|
|
66
|
+
}): AgentDiscoveryDocument => ({
|
|
67
|
+
$schema: ABSOLUTE_AGENT_SCHEMA,
|
|
68
|
+
id: \`\${origin}/agents/main\`,
|
|
69
|
+
name: 'Replace with your agent name',
|
|
70
|
+
description: 'Replace with a precise description of the outcomes this agent delivers.',
|
|
71
|
+
version,
|
|
72
|
+
url: origin,
|
|
73
|
+
publisher: {
|
|
74
|
+
id: origin,
|
|
75
|
+
name: 'Replace with your organization',
|
|
76
|
+
jwksUri: \`\${origin}/.well-known/jwks.json\`
|
|
77
|
+
},
|
|
78
|
+
capabilities: [{
|
|
79
|
+
id: 'status.read',
|
|
80
|
+
title: 'Read service status',
|
|
81
|
+
description: 'Returns the public health and readiness status of this agent.',
|
|
82
|
+
tags: ['status', 'health', 'readiness'],
|
|
83
|
+
effects: ['read'],
|
|
84
|
+
approval: 'never'
|
|
85
|
+
}],
|
|
86
|
+
interfaces: [{
|
|
87
|
+
type: 'http',
|
|
88
|
+
url: \`\${origin}/api/agents/main\`,
|
|
89
|
+
contentTypes: ['application/json']
|
|
90
|
+
}],
|
|
91
|
+
categories: ['replace-with-domain-category'],
|
|
92
|
+
tags: ['replace-with-user-intent', 'replace-with-outcome'],
|
|
93
|
+
languages: ['en'],
|
|
94
|
+
documentationUrl: \`\${origin}/docs/agents/main\`,
|
|
95
|
+
examples: [{
|
|
96
|
+
title: 'Check whether the agent is ready',
|
|
97
|
+
prompt: 'Check the agent service status.',
|
|
98
|
+
capabilityId: 'status.read'
|
|
99
|
+
}],
|
|
100
|
+
createdAt: publishedAt,
|
|
101
|
+
updatedAt: publishedAt
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
// Use a KMS/HSM-backed signer in production. Mount the returned fetch handler
|
|
105
|
+
// at the origin root so all well-known, A2A, JSON-LD, agents.txt, and sitemap
|
|
106
|
+
// discovery surfaces are served from one signed descriptor.
|
|
107
|
+
export const createAgentDiscovery = async ({
|
|
108
|
+
signer,
|
|
109
|
+
origin,
|
|
110
|
+
publishedAt,
|
|
111
|
+
version
|
|
112
|
+
}: {
|
|
113
|
+
signer: DiscoverySigner
|
|
114
|
+
origin: string
|
|
115
|
+
publishedAt: string
|
|
116
|
+
version: string
|
|
117
|
+
}) =>
|
|
118
|
+
createAgentDiscoveryHandler({
|
|
119
|
+
documents: [await signAgentDocument(
|
|
120
|
+
createAgentDocument({ origin, publishedAt, version }),
|
|
121
|
+
signer
|
|
122
|
+
)]
|
|
123
|
+
})
|
|
124
|
+
`;
|
|
125
|
+
export const agentsGuide = `# Agent execution contract
|
|
126
|
+
|
|
127
|
+
This project uses the AbsoluteJS provider-neutral agent stack.
|
|
128
|
+
|
|
129
|
+
- Authenticate agents and bind every delegation to a user with
|
|
130
|
+
\`@absolutejs/auth\`. Never treat model-provided identity as authenticated.
|
|
131
|
+
- Route every effectful tool through \`agency\` in \`src/backend/agent.ts\`.
|
|
132
|
+
Approval is exact-input-bound; execution requires a fresh single-use lease.
|
|
133
|
+
- Declare tool effects, scopes, approval policy, idempotency keys,
|
|
134
|
+
reversibility, destinations, and spend fields in manifest contract 2.
|
|
135
|
+
- Keep raw credentials host-side with \`@absolutejs/secrets\` credential
|
|
136
|
+
operations. Agents receive operation results, never secret values.
|
|
137
|
+
- Give agents bounded wallet allowances and signed mandates. The host resolves
|
|
138
|
+
ledger destinations; an agent never supplies the recipient account id.
|
|
139
|
+
- Bind MCP task handles to the authenticated actor on get, update, and cancel.
|
|
140
|
+
- Use \`@absolutejs/execution\` for idempotent effects and its transactional
|
|
141
|
+
PostgreSQL outbox before handing work to a durable queue.
|
|
142
|
+
- Publish and consume remote agents with \`@absolutejs/a2a\` using A2A 1.0;
|
|
143
|
+
preserve the authenticated tenant and actor binding at every task boundary.
|
|
144
|
+
- Route outbound HTTP through \`@absolutejs/egress\`. Authorize host, method,
|
|
145
|
+
resolved public IP, redirects, byte limits, and injected credentials host-side.
|
|
146
|
+
- Store immutable, digest-addressed policy revisions with \`@absolutejs/policy\`
|
|
147
|
+
and atomically activate only reviewed versions.
|
|
148
|
+
- Publish the signed descriptor in \`src/backend/agent-discovery.ts\` through
|
|
149
|
+
\`@absolutejs/agent-discovery\`. Keep capabilities, examples, tags, effects,
|
|
150
|
+
scopes, A2A/MCP interfaces, JSON-LD, agents.txt, and sitemap surfaces current.
|
|
151
|
+
- Run long-lived work through \`@absolutejs/agent-runtime\`; use its leases,
|
|
152
|
+
checkpoints, timers, budgets, cancellation, and crash-safe effect recovery.
|
|
153
|
+
- Authorize all HTTP/filesystem/process access with expiring
|
|
154
|
+
\`@absolutejs/agent-sandbox\` grants. There is no ambient agent authority.
|
|
155
|
+
- Preserve instruction/data separation and provenance taints with
|
|
156
|
+
\`@absolutejs/agent-trust\`; external content never becomes an instruction.
|
|
157
|
+
- Store scoped, expiring, provenance-bearing data with
|
|
158
|
+
\`@absolutejs/agent-memory\`, and validate writes against memory poisoning.
|
|
159
|
+
- Receive only verified events through \`@absolutejs/agent-inbox\`; durable
|
|
160
|
+
leases, retries, dead letters, and schedules do not require Redis.
|
|
161
|
+
- Use OpenID AuthZEN AARP for requestable approvals and COAZ mappings for
|
|
162
|
+
parameter-level MCP authorization. Approval always triggers re-evaluation.
|
|
163
|
+
- Protect operator actions with \`@absolutejs/agent-control\` scopes, a
|
|
164
|
+
kill-switch-first check, and leased idempotency records.
|
|
165
|
+
- Use \`@absolutejs/sync-bus-pg\` for durable framework channels. Redis is an
|
|
166
|
+
optional at-most-once fanout adapter, not a source of truth or work queue.
|
|
167
|
+
- Run \`@absolutejs/agent-conformance\` suites for every new action, capability,
|
|
168
|
+
credential, wallet, egress, execution, control, and task adapter.
|
|
169
|
+
- Use the control plane kill switch for incident response. It blocks new
|
|
170
|
+
requests, lease issuance, and execution before downstream revocation fans out.
|
|
171
|
+
|
|
172
|
+
Memory stores are for local development only. Production stores must be
|
|
173
|
+
durable and enforce lease/capability consumption atomically. Apply each
|
|
174
|
+
package's exported PostgreSQL schema in a migration before enabling traffic.
|
|
175
|
+
`;
|
|
176
|
+
export const scaffoldAgentic = ({ backendDirectory, projectName }) => {
|
|
177
|
+
mkdirSync(backendDirectory, { recursive: true });
|
|
178
|
+
writeFileSync(join(backendDirectory, 'agent.ts'), agentRuntimeSource);
|
|
179
|
+
writeFileSync(join(backendDirectory, 'agent-discovery.ts'), agentDiscoverySource);
|
|
180
|
+
writeFileSync(join(projectName, 'AGENTS.md'), agentsGuide);
|
|
181
|
+
};
|
|
@@ -2,6 +2,7 @@ import { CreateConfiguration } from '../../types';
|
|
|
2
2
|
type ScaffoldBackendProps = Pick<CreateConfiguration, 'assetsDirectory' | 'absProviders' | 'authOption' | 'buildDirectory' | 'databaseEngine' | 'databaseHost' | 'frontendDirectories' | 'orm' | 'plugins' | 'tailwind'> & {
|
|
3
3
|
backendDirectory: string;
|
|
4
4
|
publicDirectory: string;
|
|
5
|
+
typesDirectory: string;
|
|
5
6
|
};
|
|
6
|
-
export declare const scaffoldBackend: ({ assetsDirectory, authOption, absProviders, backendDirectory, buildDirectory, databaseEngine, databaseHost, frontendDirectories, orm, plugins, publicDirectory, tailwind }: ScaffoldBackendProps) => void;
|
|
7
|
+
export declare const scaffoldBackend: ({ assetsDirectory, authOption, absProviders, backendDirectory, buildDirectory, databaseEngine, databaseHost, frontendDirectories, orm, plugins, publicDirectory, tailwind, typesDirectory }: ScaffoldBackendProps) => void;
|
|
7
8
|
export {};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { mkdirSync, writeFileSync } from 'fs';
|
|
2
2
|
import { join } from 'path';
|
|
3
|
-
import { generateAbsoluteAuthConfig } from './generateAbsoluteAuthConfig';
|
|
3
|
+
import { generateAbsoluteAuthConfig, generateSessionUserType } from './generateAbsoluteAuthConfig';
|
|
4
4
|
import { generateServerFile } from './generateServer';
|
|
5
|
-
export const scaffoldBackend = ({ assetsDirectory, authOption, absProviders, backendDirectory, buildDirectory, databaseEngine, databaseHost, frontendDirectories, orm, plugins, publicDirectory, tailwind }) => {
|
|
5
|
+
export const scaffoldBackend = ({ assetsDirectory, authOption, absProviders, backendDirectory, buildDirectory, databaseEngine, databaseHost, frontendDirectories, orm, plugins, publicDirectory, tailwind, typesDirectory }) => {
|
|
6
6
|
generateServerFile({
|
|
7
7
|
assetsDirectory,
|
|
8
8
|
authOption,
|
|
@@ -21,5 +21,13 @@ export const scaffoldBackend = ({ assetsDirectory, authOption, absProviders, bac
|
|
|
21
21
|
const hasDatabase = databaseEngine !== undefined && databaseEngine !== 'none';
|
|
22
22
|
const absoluteAuthConfig = generateAbsoluteAuthConfig(absProviders, hasDatabase);
|
|
23
23
|
writeFileSync(join(backendDirectory, 'utils', 'absoluteAuthConfig.ts'), absoluteAuthConfig, 'utf-8');
|
|
24
|
+
/* The auth config and the example page both import `User` from
|
|
25
|
+
types/databaseTypes, which scaffoldDatabase only writes when there is
|
|
26
|
+
a database. Without one, emit the session user shape on its own so the
|
|
27
|
+
auth scaffold still type-checks. */
|
|
28
|
+
if (!hasDatabase) {
|
|
29
|
+
mkdirSync(typesDirectory, { recursive: true });
|
|
30
|
+
writeFileSync(join(typesDirectory, 'databaseTypes.ts'), generateSessionUserType(), 'utf-8');
|
|
31
|
+
}
|
|
24
32
|
}
|
|
25
33
|
};
|
|
@@ -40,6 +40,7 @@ export const scaffoldFrontends = ({ frontendDirectory, assetsDirectory, absProvi
|
|
|
40
40
|
absProviders,
|
|
41
41
|
assetsDirectory,
|
|
42
42
|
authOption,
|
|
43
|
+
directory,
|
|
43
44
|
editBasePath,
|
|
44
45
|
frontends,
|
|
45
46
|
includeExamples,
|
|
@@ -56,6 +57,7 @@ export const scaffoldFrontends = ({ frontendDirectory, assetsDirectory, absProvi
|
|
|
56
57
|
absProviders,
|
|
57
58
|
assetsDirectory,
|
|
58
59
|
authOption,
|
|
60
|
+
directory,
|
|
59
61
|
editBasePath,
|
|
60
62
|
frontends,
|
|
61
63
|
includeExamples,
|
|
@@ -72,6 +74,7 @@ export const scaffoldFrontends = ({ frontendDirectory, assetsDirectory, absProvi
|
|
|
72
74
|
absProviders,
|
|
73
75
|
assetsDirectory,
|
|
74
76
|
authOption,
|
|
77
|
+
directory,
|
|
75
78
|
editBasePath,
|
|
76
79
|
frontends,
|
|
77
80
|
includeExamples,
|
|
@@ -88,6 +91,7 @@ export const scaffoldFrontends = ({ frontendDirectory, assetsDirectory, absProvi
|
|
|
88
91
|
absProviders,
|
|
89
92
|
assetsDirectory,
|
|
90
93
|
authOption,
|
|
94
|
+
directory,
|
|
91
95
|
editBasePath,
|
|
92
96
|
frontends,
|
|
93
97
|
includeExamples,
|
|
@@ -104,6 +108,7 @@ export const scaffoldFrontends = ({ frontendDirectory, assetsDirectory, absProvi
|
|
|
104
108
|
absProviders,
|
|
105
109
|
assetsDirectory,
|
|
106
110
|
authOption,
|
|
111
|
+
directory,
|
|
107
112
|
editBasePath,
|
|
108
113
|
frontends,
|
|
109
114
|
includeExamples,
|
|
@@ -121,6 +126,7 @@ export const scaffoldFrontends = ({ frontendDirectory, assetsDirectory, absProvi
|
|
|
121
126
|
absProviders,
|
|
122
127
|
assetsDirectory,
|
|
123
128
|
authOption,
|
|
129
|
+
directory,
|
|
124
130
|
editBasePath,
|
|
125
131
|
frontends,
|
|
126
132
|
includeExamples,
|
|
@@ -2,5 +2,5 @@ import { ProviderOption } from '@absolutejs/auth';
|
|
|
2
2
|
import { AuthOption, Frontend } from '../../types';
|
|
3
3
|
export declare const generateAppComponent: (frontends: Frontend[], editBasePath: string) => string;
|
|
4
4
|
export declare const generateDropdownComponent: (frontends: Frontend[]) => string;
|
|
5
|
-
export declare const generateReactExamplePage: (authOption: AuthOption, includeExamples: boolean) => string;
|
|
5
|
+
export declare const generateReactExamplePage: (authOption: AuthOption, includeExamples: boolean, reactDirectory: string) => string;
|
|
6
6
|
export declare const generateSignInComponent: (absProviders: ProviderOption[] | undefined) => string;
|
|
@@ -73,10 +73,14 @@ export const generateDropdownComponent = (frontends) => {
|
|
|
73
73
|
);
|
|
74
74
|
`;
|
|
75
75
|
};
|
|
76
|
-
|
|
76
|
+
/* Pages live at `src/frontend/<reactDirectory>/pages`, but `reactDirectory` is
|
|
77
|
+
empty when React is the only frontend, which moves the page one level up. */
|
|
78
|
+
const buildDatabaseTypesPath = (reactDirectory) => `${reactDirectory ? '../../../' : '../../'}types/databaseTypes`;
|
|
79
|
+
export const generateReactExamplePage = (authOption, includeExamples, reactDirectory) => {
|
|
80
|
+
const databaseTypesPath = buildDatabaseTypesPath(reactDirectory);
|
|
77
81
|
if (!includeExamples) {
|
|
78
82
|
const authImports = authOption === 'abs'
|
|
79
|
-
? `import type { User } from '
|
|
83
|
+
? `import type { User } from '${databaseTypesPath}';\nimport type { ProviderConfiguration } from '@absolutejs/auth';\n`
|
|
80
84
|
: '';
|
|
81
85
|
const authProps = authOption === 'abs'
|
|
82
86
|
? '\n\tuser: User | null;\n\tproviderConfiguration: ProviderConfiguration | undefined;'
|
|
@@ -146,7 +150,7 @@ export const ReactExample = ({ cssPath }: ReactExampleProps) => (
|
|
|
146
150
|
: `<Dropdown />`;
|
|
147
151
|
const closing = authOption === 'abs' ? `};` : `);`;
|
|
148
152
|
return `
|
|
149
|
-
${authOption === 'abs' ? `import type { User } from '
|
|
153
|
+
${authOption === 'abs' ? `import type { User } from '${databaseTypesPath}';\nimport { extractPropFromIdentity } from '@absolutejs/auth';\nimport type { ProviderConfiguration } from '@absolutejs/auth';` : ''}
|
|
150
154
|
import { App } from '../components/App';
|
|
151
155
|
import { Dropdown } from '../components/Dropdown';
|
|
152
156
|
import { Head } from '../components/Head';
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { ScaffoldFrontendProps } from '../../types';
|
|
2
|
-
export declare const scaffoldReact: ({ authOption, editBasePath, includeExamples, targetDirectory, templatesDirectory, frontends, projectAssetsDirectory, absProviders, stylesIndexesDirectory }: ScaffoldFrontendProps) => void;
|
|
2
|
+
export declare const scaffoldReact: ({ authOption, directory, editBasePath, includeExamples, targetDirectory, templatesDirectory, frontends, projectAssetsDirectory, absProviders, stylesIndexesDirectory }: ScaffoldFrontendProps) => void;
|
|
@@ -1,20 +1,25 @@
|
|
|
1
1
|
import { copyFileSync, cpSync, mkdirSync, writeFileSync } from 'fs';
|
|
2
|
-
import { join } from 'path';
|
|
2
|
+
import { basename, join } from 'path';
|
|
3
3
|
import { generateMarkupCSS } from '../project/generateMarkupCSS';
|
|
4
4
|
import { generateAppComponent, generateDropdownComponent, generateReactExamplePage, generateSignInComponent } from './generateReactComponents';
|
|
5
|
-
export const scaffoldReact = ({ authOption, editBasePath, includeExamples, targetDirectory, templatesDirectory, frontends, projectAssetsDirectory, absProviders, stylesIndexesDirectory }) => {
|
|
5
|
+
export const scaffoldReact = ({ authOption, directory, editBasePath, includeExamples, targetDirectory, templatesDirectory, frontends, projectAssetsDirectory, absProviders, stylesIndexesDirectory }) => {
|
|
6
6
|
if (!includeExamples) {
|
|
7
7
|
mkdirSync(join(targetDirectory, 'components'), { recursive: true });
|
|
8
8
|
copyFileSync(join(templatesDirectory, 'react', 'components', 'Head.tsx'), join(targetDirectory, 'components', 'Head.tsx'));
|
|
9
9
|
mkdirSync(join(targetDirectory, 'pages'), { recursive: true });
|
|
10
|
-
writeFileSync(join(targetDirectory, 'pages', 'ReactExample.tsx'), generateReactExamplePage(authOption, false), 'utf-8');
|
|
10
|
+
writeFileSync(join(targetDirectory, 'pages', 'ReactExample.tsx'), generateReactExamplePage(authOption, false, directory), 'utf-8');
|
|
11
11
|
writeFileSync(join(stylesIndexesDirectory, 'react-example.css'), `@import url('../reset.css');`, 'utf-8');
|
|
12
12
|
return;
|
|
13
13
|
}
|
|
14
14
|
mkdirSync(join(projectAssetsDirectory, 'svg'), { recursive: true });
|
|
15
15
|
copyFileSync(join(templatesDirectory, 'assets', 'svg', 'react.svg'), join(projectAssetsDirectory, 'svg', 'react.svg'));
|
|
16
16
|
copyFileSync(join(templatesDirectory, 'assets', 'svg', 'google-logo.svg'), join(projectAssetsDirectory, 'svg', 'google-logo.svg'));
|
|
17
|
+
/* `OAuthLink` is only rendered by the generated `SignIn` component, and it
|
|
18
|
+
types its provider against `citra`, which only ships as a transitive
|
|
19
|
+
dependency of `@absolutejs/auth`. Without Absolute Auth the import cannot
|
|
20
|
+
resolve, so the component is left out entirely. */
|
|
17
21
|
cpSync(join(templatesDirectory, 'react'), targetDirectory, {
|
|
22
|
+
filter: (source) => authOption === 'abs' || basename(source) !== 'OAuthLink.tsx',
|
|
18
23
|
recursive: true
|
|
19
24
|
});
|
|
20
25
|
const appComponent = generateAppComponent(frontends, editBasePath);
|
|
@@ -25,7 +30,7 @@ export const scaffoldReact = ({ authOption, editBasePath, includeExamples, targe
|
|
|
25
30
|
const signInComponent = generateSignInComponent(absProviders);
|
|
26
31
|
writeFileSync(join(targetDirectory, 'components', 'SignIn.tsx'), signInComponent, 'utf-8');
|
|
27
32
|
}
|
|
28
|
-
const pageComponent = generateReactExamplePage(authOption, true);
|
|
33
|
+
const pageComponent = generateReactExamplePage(authOption, true, directory);
|
|
29
34
|
mkdirSync(join(targetDirectory, 'pages'), { recursive: true });
|
|
30
35
|
writeFileSync(join(targetDirectory, 'pages', 'ReactExample.tsx'), pageComponent, 'utf-8');
|
|
31
36
|
const cssOutputFile = join(stylesIndexesDirectory, 'react-example.css');
|
package/dist/messages.d.ts
CHANGED
|
@@ -10,5 +10,5 @@ type DebugMessageProps = {
|
|
|
10
10
|
response: CreateConfiguration;
|
|
11
11
|
packageManager: string;
|
|
12
12
|
};
|
|
13
|
-
export declare const getDebugMessage: ({ response: { projectName, codeQualityTool, directoryConfig, useTailwind, tailwind, frontends, includeExamples, useHTMLScripts, frontendDirectories, buildDirectory, assetsDirectory, databaseEngine, databaseHost, databaseDirectory, orm, authOption, plugins, initializeGitNow, installDependenciesNow }, packageManager }: DebugMessageProps) => string;
|
|
13
|
+
export declare const getDebugMessage: ({ response: { agentic, projectName, codeQualityTool, directoryConfig, useTailwind, tailwind, frontends, includeExamples, useHTMLScripts, frontendDirectories, buildDirectory, assetsDirectory, databaseEngine, databaseHost, databaseDirectory, orm, authOption, plugins, initializeGitNow, installDependenciesNow }, packageManager }: DebugMessageProps) => string;
|
|
14
14
|
export {};
|
package/dist/messages.js
CHANGED
|
@@ -12,6 +12,7 @@ Options:
|
|
|
12
12
|
${cyan('--debug, -d')} Display a summary of the project configuration after creation
|
|
13
13
|
|
|
14
14
|
${cyan('--abs-provider')} A provider for Absolute-Auth (eg. 'google', 'github', 'discord') the full list is available at https://absolutejs.com/documentation/absolute-auth
|
|
15
|
+
${cyan('--agentic')} Add provider-neutral agent auth, actions, MCP, wallet, credential, and conformance scaffolding
|
|
15
16
|
${cyan('--angular')} Include an Angular frontend
|
|
16
17
|
${cyan('--angular-dir')} ${dim(cyan('<directory>'))} Specify the directory for and use the Angular frontend
|
|
17
18
|
${cyan('--assets')} ${dim(cyan('<directory>'))} Directory name for your static assets
|
|
@@ -49,7 +50,7 @@ export const getOutroMessage = ({ projectName, packageManager, installDependenci
|
|
|
49
50
|
`${cyan('cd')} ${projectName}\n` +
|
|
50
51
|
`${installDependenciesNow ? '' : `${cyan(`${packageManager} install`)}\n`}` +
|
|
51
52
|
`${cyan(`${packageManager} dev`)}`; // TODO: Some package managers need run
|
|
52
|
-
export const getDebugMessage = ({ response: { projectName, codeQualityTool, directoryConfig, useTailwind, tailwind, frontends, includeExamples, useHTMLScripts, frontendDirectories, buildDirectory, assetsDirectory, databaseEngine, databaseHost, databaseDirectory, orm, authOption, plugins, initializeGitNow, installDependenciesNow }, packageManager }) => {
|
|
53
|
+
export const getDebugMessage = ({ response: { agentic, projectName, codeQualityTool, directoryConfig, useTailwind, tailwind, frontends, includeExamples, useHTMLScripts, frontendDirectories, buildDirectory, assetsDirectory, databaseEngine, databaseHost, databaseDirectory, orm, authOption, plugins, initializeGitNow, installDependenciesNow }, packageManager }) => {
|
|
53
54
|
const htmlScriptingValue = useHTMLScripts
|
|
54
55
|
? blueBright('TypeScript')
|
|
55
56
|
: dim('None');
|
|
@@ -63,6 +64,7 @@ export const getDebugMessage = ({ response: { projectName, codeQualityTool, dire
|
|
|
63
64
|
/* prettier-ignore */
|
|
64
65
|
const lines = [
|
|
65
66
|
['Project Name', projectName],
|
|
67
|
+
['Agent-first Stack', agentic ? green('Yes') : dim('No')],
|
|
66
68
|
['Package Manager', packageManager],
|
|
67
69
|
['Config Type', isCustomConfig ? green('Custom') : dim('Default')],
|
|
68
70
|
['Linting', codeQualityTool === 'eslint+prettier' ? 'ESLint + Prettier' : 'Biome'],
|
package/dist/prompt.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getAuthOption } from './questions/authOption';
|
|
2
|
+
import { getAgentic } from './questions/agentic';
|
|
2
3
|
import { getCodeQualityTool } from './questions/codeQualityTool';
|
|
3
4
|
import { getConfigurationType } from './questions/configurationType';
|
|
4
5
|
import { getDatabaseEngine } from './questions/databaseEngine';
|
|
@@ -18,11 +19,14 @@ import { getUseTailwind } from './questions/useTailwind';
|
|
|
18
19
|
import { orPrompt } from './utils/interactive';
|
|
19
20
|
export const prompt = async (argumentConfiguration) => {
|
|
20
21
|
// 1. Project name
|
|
21
|
-
const projectName = argumentConfiguration.projectName ??
|
|
22
|
+
const projectName = argumentConfiguration.projectName ??
|
|
23
|
+
(await orPrompt('a project name', getProjectName));
|
|
22
24
|
// 2. Linting/formatting tool
|
|
23
|
-
const codeQualityTool = argumentConfiguration.codeQualityTool ??
|
|
25
|
+
const codeQualityTool = argumentConfiguration.codeQualityTool ??
|
|
26
|
+
(await orPrompt('--eslint+prettier/--biome', getCodeQualityTool));
|
|
24
27
|
// 3. Tailwind support?
|
|
25
|
-
const useTailwind = argumentConfiguration.useTailwind ??
|
|
28
|
+
const useTailwind = argumentConfiguration.useTailwind ??
|
|
29
|
+
(await orPrompt('--tailwind/--no-tailwind', getUseTailwind));
|
|
26
30
|
// 4. Frontend(s)
|
|
27
31
|
const frontends = argumentConfiguration.frontends?.filter((frontend) => frontend !== undefined) ?? (await orPrompt('--react/--vue/--svelte/…', getFrontends));
|
|
28
32
|
// 5. HTML scripting option (if HTML was selected)
|
|
@@ -31,18 +35,22 @@ export const prompt = async (argumentConfiguration) => {
|
|
|
31
35
|
(await orPrompt('--html-scripts/--no-html-scripts', getHtmlScriptingOption)))
|
|
32
36
|
: false;
|
|
33
37
|
// 5b. Include example pages/components, or generate a bare skeleton
|
|
34
|
-
const includeExamples = argumentConfiguration.includeExamples ??
|
|
38
|
+
const includeExamples = argumentConfiguration.includeExamples ??
|
|
39
|
+
(await orPrompt('--examples/--no-examples', getIncludeExamples));
|
|
35
40
|
// 6. Database engine
|
|
36
|
-
const databaseEngine = argumentConfiguration.databaseEngine ??
|
|
41
|
+
const databaseEngine = argumentConfiguration.databaseEngine ??
|
|
42
|
+
(await orPrompt('--db', getDatabaseEngine));
|
|
37
43
|
// 7. Database host
|
|
38
44
|
const databaseHost = argumentConfiguration.databaseHost ??
|
|
39
45
|
(await orPrompt('--db-host', () => getDatabaseHost(databaseEngine)));
|
|
40
46
|
// 8. ORM choice
|
|
41
47
|
const orm = databaseEngine !== undefined && databaseEngine !== 'none'
|
|
42
|
-
? (argumentConfiguration.orm ??
|
|
48
|
+
? (argumentConfiguration.orm ??
|
|
49
|
+
(await orPrompt('--orm', () => getORM(databaseEngine))))
|
|
43
50
|
: undefined;
|
|
44
51
|
// 9. Configuration type
|
|
45
|
-
let directoryConfig = argumentConfiguration.directoryConfig ??
|
|
52
|
+
let directoryConfig = argumentConfiguration.directoryConfig ??
|
|
53
|
+
(await orPrompt('--directory', getConfigurationType));
|
|
46
54
|
// 10. Directory configurations
|
|
47
55
|
const { buildDirectory, assetsDirectory, tailwind, databaseDirectory } = await getDirectoryConfiguration({
|
|
48
56
|
argumentConfiguration,
|
|
@@ -56,11 +64,16 @@ export const prompt = async (argumentConfiguration) => {
|
|
|
56
64
|
if (argumentConfiguration.frontendDirectories !== undefined)
|
|
57
65
|
directoryConfig = 'custom';
|
|
58
66
|
// 12. Auth provider
|
|
59
|
-
const authOption = argumentConfiguration.authOption ??
|
|
67
|
+
const authOption = argumentConfiguration.authOption ??
|
|
68
|
+
(await orPrompt('--auth', getAuthOption));
|
|
69
|
+
// 12b. Agent-first action/auth/MCP/wallet/credential stack
|
|
70
|
+
const agentic = argumentConfiguration.agentic ??
|
|
71
|
+
(await orPrompt('--agentic/--no-agentic', getAgentic));
|
|
60
72
|
// 13. Additional plugins
|
|
61
73
|
const plugins = argumentConfiguration.plugins?.filter((plugin) => plugin !== undefined) ?? (await orPrompt('--plugin', getPlugins));
|
|
62
74
|
// 14. Initialize Git repository
|
|
63
|
-
const initializeGitNow = argumentConfiguration.initializeGitNow ??
|
|
75
|
+
const initializeGitNow = argumentConfiguration.initializeGitNow ??
|
|
76
|
+
(await orPrompt('--git/--no-git', getInitializeGit));
|
|
64
77
|
// 14b. Optionally connect the new project to GitHub
|
|
65
78
|
const resolveGithubLink = async () => {
|
|
66
79
|
if (!initializeGitNow) {
|
|
@@ -84,6 +97,7 @@ export const prompt = async (argumentConfiguration) => {
|
|
|
84
97
|
const installDependenciesNow = argumentConfiguration.installDependenciesNow ??
|
|
85
98
|
(await orPrompt('--install/--no-install', getInstallDependencies));
|
|
86
99
|
const values = {
|
|
100
|
+
agentic,
|
|
87
101
|
absProviders: argumentConfiguration.absProviders?.filter((provider) => provider !== undefined),
|
|
88
102
|
assetsDirectory,
|
|
89
103
|
authOption,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const getAgentic: () => Promise<boolean>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { confirm, isCancel } from '@clack/prompts';
|
|
2
|
+
import { abort } from '../utils/abort';
|
|
3
|
+
export const getAgentic = async () => {
|
|
4
|
+
const agentic = await confirm({
|
|
5
|
+
initialValue: false,
|
|
6
|
+
message: 'Add the agent-first stack (auth, actions, MCP, wallet, credentials, and conformance)?'
|
|
7
|
+
});
|
|
8
|
+
if (isCancel(agentic))
|
|
9
|
+
abort();
|
|
10
|
+
return agentic;
|
|
11
|
+
};
|
package/dist/scaffold.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ type ScaffoldProps = {
|
|
|
5
5
|
latest: boolean;
|
|
6
6
|
envVariables: string[] | undefined;
|
|
7
7
|
};
|
|
8
|
-
export declare const scaffold: ({ response: { projectName, codeQualityTool, initializeGitNow, githubLink, githubRepoUrl, githubVisibility, databaseEngine, databaseHost, useHTMLScripts, useTailwind, databaseDirectory, absProviders, includeExamples, orm, frontends, plugins, authOption, buildDirectory, assetsDirectory, tailwind, installDependenciesNow, frontendDirectories }, latest, envVariables, packageManager }: ScaffoldProps) => Promise<{
|
|
8
|
+
export declare const scaffold: ({ response: { agentic, projectName, codeQualityTool, initializeGitNow, githubLink, githubRepoUrl, githubVisibility, databaseEngine, databaseHost, useHTMLScripts, useTailwind, databaseDirectory, absProviders, includeExamples, orm, frontends, plugins, authOption, buildDirectory, assetsDirectory, tailwind, installDependenciesNow, frontendDirectories }, latest, envVariables, packageManager }: ScaffoldProps) => Promise<{
|
|
9
9
|
dockerFreshInstall: boolean;
|
|
10
10
|
}>;
|
|
11
11
|
export {};
|
package/dist/scaffold.js
CHANGED
|
@@ -10,7 +10,8 @@ import { scaffoldConfigurationFiles } from './generators/configurations/scaffold
|
|
|
10
10
|
import { scaffoldDatabase } from './generators/db/scaffoldDatabase';
|
|
11
11
|
import { scaffoldBackend } from './generators/project/scaffoldBackend';
|
|
12
12
|
import { scaffoldFrontends } from './generators/project/scaffoldFrontends';
|
|
13
|
-
|
|
13
|
+
import { scaffoldAgentic } from './generators/project/scaffoldAgentic';
|
|
14
|
+
export const scaffold = async ({ response: { agentic, projectName, codeQualityTool, initializeGitNow, githubLink, githubRepoUrl, githubVisibility, databaseEngine, databaseHost, useHTMLScripts, useTailwind, databaseDirectory, absProviders, includeExamples, orm, frontends, plugins, authOption, buildDirectory, assetsDirectory, tailwind, installDependenciesNow, frontendDirectories }, latest, envVariables, packageManager }) => {
|
|
14
15
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
15
16
|
const templatesDirectory = join(__dirname, '/templates');
|
|
16
17
|
const { frontendDirectory, backendDirectory, projectAssetsDirectory, typesDirectory } = initalizeRoot(projectName, templatesDirectory);
|
|
@@ -27,6 +28,7 @@ export const scaffold = async ({ response: { projectName, codeQualityTool, initi
|
|
|
27
28
|
templatesDirectory
|
|
28
29
|
});
|
|
29
30
|
await createPackageJson({
|
|
31
|
+
agentic,
|
|
30
32
|
authOption,
|
|
31
33
|
codeQualityTool,
|
|
32
34
|
databaseEngine,
|
|
@@ -51,8 +53,11 @@ export const scaffold = async ({ response: { projectName, codeQualityTool, initi
|
|
|
51
53
|
orm,
|
|
52
54
|
plugins,
|
|
53
55
|
publicDirectory: 'public',
|
|
54
|
-
tailwind
|
|
56
|
+
tailwind,
|
|
57
|
+
typesDirectory
|
|
55
58
|
});
|
|
59
|
+
if (agentic)
|
|
60
|
+
scaffoldAgentic({ backendDirectory, projectName });
|
|
56
61
|
let dockerFreshInstall = false;
|
|
57
62
|
if (databaseDirectory !== undefined &&
|
|
58
63
|
databaseEngine !== 'none' &&
|
package/dist/types.d.ts
CHANGED
|
@@ -4,6 +4,9 @@ export type ScaffoldFrontendProps = {
|
|
|
4
4
|
absProviders: ProviderOption[] | undefined;
|
|
5
5
|
assetsDirectory: string;
|
|
6
6
|
authOption: AuthOption;
|
|
7
|
+
/** Frontend's directory under `src/frontend`; empty when it is the only
|
|
8
|
+
* frontend and no directory was configured for it. */
|
|
9
|
+
directory: string;
|
|
7
10
|
editBasePath: string;
|
|
8
11
|
includeExamples: boolean;
|
|
9
12
|
targetDirectory: string;
|
|
@@ -43,6 +46,7 @@ export type TailwindConfig = {
|
|
|
43
46
|
} | undefined;
|
|
44
47
|
export type GithubLinkOption = 'existing' | 'create' | 'skip';
|
|
45
48
|
export type CreateConfiguration = {
|
|
49
|
+
agentic: boolean;
|
|
46
50
|
absProviders: ProviderOption[] | undefined;
|
|
47
51
|
assetsDirectory: string;
|
|
48
52
|
authOption: AuthOption;
|
|
@@ -12,6 +12,7 @@ export const parseCommandLineOptions = () => {
|
|
|
12
12
|
args: argv.slice(DEFAULT_ARG_LENGTH),
|
|
13
13
|
options: {
|
|
14
14
|
'abs-provider': { multiple: true, type: 'string' },
|
|
15
|
+
agentic: { type: 'boolean' },
|
|
15
16
|
angular: { type: 'boolean' },
|
|
16
17
|
'angular-dir': { type: 'string' },
|
|
17
18
|
assets: { type: 'string' },
|
|
@@ -131,7 +132,9 @@ export const parseCommandLineOptions = () => {
|
|
|
131
132
|
? values.directory
|
|
132
133
|
: // --skip defaults every prompted axis (headless callers hang on
|
|
133
134
|
// missing ones).
|
|
134
|
-
|
|
135
|
+
values.skip
|
|
136
|
+
? 'default'
|
|
137
|
+
: undefined;
|
|
135
138
|
if (values.directory !== undefined && directoryConfig === undefined) {
|
|
136
139
|
errors.push(`Invalid directory configuration: "${values.directory}". Expected: [ ${availableDirectoryConfigurations.join(', ')} ]`);
|
|
137
140
|
}
|
|
@@ -222,7 +225,8 @@ export const parseCommandLineOptions = () => {
|
|
|
222
225
|
let tailwind = hasTailwindFiles
|
|
223
226
|
? { input: values['tailwind-input'], output: values['tailwind-output'] }
|
|
224
227
|
: undefined;
|
|
225
|
-
const useTailwind = values.tailwind ??
|
|
228
|
+
const useTailwind = values.tailwind ??
|
|
229
|
+
(hasTailwindFiles ? true : values.skip ? false : undefined);
|
|
226
230
|
if (useTailwind === false && hasTailwindFiles) {
|
|
227
231
|
console.warn('Warning: Tailwind CSS input/output files are specified but Tailwind is disabled.');
|
|
228
232
|
tailwind = undefined;
|
|
@@ -250,6 +254,7 @@ export const parseCommandLineOptions = () => {
|
|
|
250
254
|
? repoVisibility
|
|
251
255
|
: undefined;
|
|
252
256
|
const argumentConfiguration = {
|
|
257
|
+
agentic: values.agentic ?? (values.skip ? false : undefined),
|
|
253
258
|
absProviders: absProviders.length ? absProviders : undefined,
|
|
254
259
|
assetsDirectory: values.assets,
|
|
255
260
|
authOption,
|
|
@@ -260,15 +265,19 @@ export const parseCommandLineOptions = () => {
|
|
|
260
265
|
databaseHost,
|
|
261
266
|
directoryConfig,
|
|
262
267
|
frontendDirectories,
|
|
263
|
-
frontends: selectedFrontends.length
|
|
268
|
+
frontends: selectedFrontends.length
|
|
269
|
+
? selectedFrontends
|
|
270
|
+
: values.skip
|
|
271
|
+
? []
|
|
272
|
+
: undefined,
|
|
264
273
|
githubLink: isGithubLinkOption(values.github)
|
|
265
274
|
? values.github
|
|
266
275
|
: undefined,
|
|
267
276
|
githubRepoUrl: normalizeRepoInput(values.repo)?.httpsUrl,
|
|
268
277
|
githubVisibility,
|
|
269
278
|
includeExamples,
|
|
270
|
-
initializeGitNow: values.git,
|
|
271
|
-
installDependenciesNow: values.install,
|
|
279
|
+
initializeGitNow: values.git ?? (values.skip ? false : undefined),
|
|
280
|
+
installDependenciesNow: values.install ?? (values.skip ? false : undefined),
|
|
272
281
|
orm,
|
|
273
282
|
plugins,
|
|
274
283
|
projectName,
|