Skip to content

Commit 12a2554

Browse files
committed
Merge branch 'main' of github.com:nanoapi-io/napi into feature/php-manifest
2 parents 44c417f + d5c2217 commit 12a2554

File tree

24 files changed

+1289
-23
lines changed

24 files changed

+1289
-23
lines changed

.github/workflows/lint_test_compile.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414
- name: Set up Deno
1515
uses: denoland/setup-deno@v2
1616
with:
17-
deno-version: "2.x"
17+
deno-version: "2.4.0"
1818

1919
- name: Install dependencies
2020
run: |
@@ -35,7 +35,7 @@ jobs:
3535
- name: Set up Node.js
3636
uses: denoland/setup-deno@v2
3737
with:
38-
deno-version: "2.x"
38+
deno-version: "2.4.0"
3939

4040
- name: Install dependencies
4141
run: |
@@ -53,7 +53,7 @@ jobs:
5353
- name: Set up Node.js
5454
uses: denoland/setup-deno@v2
5555
with:
56-
deno-version: "2.x"
56+
deno-version: "2.4.0"
5757

5858
- name: Install dependencies
5959
run: |

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
- name: Set up Deno
1717
uses: denoland/setup-deno@v2
1818
with:
19-
deno-version: "2.3.5"
19+
deno-version: "2.4.0"
2020

2121
- name: Get new version and bump deno.json
2222
id: get_version

deno.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
{
2-
"version": "1.0.11",
2+
"version": "1.0.14",
33
"name": "@napi/cli",
44
"exports": "./src/index.ts",
55
"nodeModulesDir": "auto",
66
"lock": false,
77
"imports": {
88
"@inquirer/prompts": "npm:@inquirer/prompts@^7.5.3",
9+
"@langchain/anthropic": "npm:@langchain/anthropic@^0.3.23",
10+
"@langchain/core": "npm:@langchain/core@^0.3.61",
11+
"@langchain/google-genai": "npm:@langchain/google-genai@^0.2.14",
12+
"@langchain/google-vertexai": "npm:@langchain/google-vertexai@^0.2.14",
13+
"@langchain/langgraph": "npm:@langchain/langgraph@^0.3.5",
14+
"@langchain/openai": "npm:@langchain/openai@^0.5.15",
915
"@oak/oak": "jsr:@oak/oak@^17.1.4",
1016
"@std/expect": "jsr:@std/expect@^1.0.16",
1117
"@std/path": "jsr:@std/path@^1.0.9",

examples/csharp/EndpointExample/Properties/launchSettings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
{
1+
{
22
"$schema": "https://json.schemastore.org/launchsettings.json",
33
"profiles": {
44
"http": {

src/cli/handlers/init/index.ts

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
getConfigFromWorkDir,
55
} from "../../middlewares/napiConfig.ts";
66
import { join, normalize, relative, SEPARATOR } from "@std/path";
7-
import type z from "zod";
7+
import z from "zod";
88
import type { localConfigSchema } from "../../middlewares/napiConfig.ts";
99
import pythonStdlibList from "../../../scripts/generate_python_stdlib_list/output.json" with {
1010
type: "json",
@@ -20,6 +20,11 @@ import {
2020
import { ApiService } from "../../../apiService/index.ts";
2121
import type { globalConfigSchema } from "../../middlewares/globalConfig.ts";
2222
import { isAuthenticatedMiddleware } from "../../middlewares/isAuthenticated.ts";
23+
import {
24+
ANTHROPIC_PROVIDER,
25+
GOOGLE_PROVIDER,
26+
OPENAI_PROVIDER,
27+
} from "../../../manifest/dependencyManifest/labeling/model.ts";
2328

2429
function builder(
2530
yargs: Arguments & {
@@ -749,12 +754,25 @@ async function createNewProject(apiService: ApiService): Promise<number> {
749754
},
750755
});
751756

757+
const projectRepoUrl = await input({
758+
message: "Enter the URL of your project repository:",
759+
validate: (value) => {
760+
if (!value.trim()) return "Project repository URL cannot be empty";
761+
const result = z.string().url().safeParse(value);
762+
if (!result.success) {
763+
return result.error.message;
764+
}
765+
return true;
766+
},
767+
});
768+
752769
try {
753770
const createProjectResponse = await apiService.performRequest(
754771
"POST",
755772
"/projects",
756773
{
757774
name: projectName,
775+
repoUrl: projectRepoUrl,
758776
workspaceId: selectedWorkspaceId,
759777
maxCodeCharPerSymbol: 100,
760778
maxCodeCharPerFile: 1000,
@@ -1018,6 +1036,61 @@ export async function generateConfig(
10181036
// Show final file selection to the user
10191037
showFinalFileSelection(workDir, includePatterns, excludePatterns);
10201038

1039+
// Labeling configuration
1040+
console.info("\n🏷️ LABELING CONFIGURATION");
1041+
console.info(
1042+
"Labeling helps categorize and organize your code dependencies using AI models.",
1043+
);
1044+
1045+
const enableLabeling = await confirm({
1046+
message: "Would you like to enable AI-powered labeling?",
1047+
default: false,
1048+
});
1049+
1050+
let labelingConfig:
1051+
| z.infer<typeof localConfigSchema>["labeling"]
1052+
| undefined = undefined;
1053+
1054+
if (enableLabeling) {
1055+
console.info("\n🤖 AI MODEL SELECTION");
1056+
console.info(
1057+
"Choose an AI provider for labeling your dependencies:",
1058+
);
1059+
1060+
const modelProvider = await select({
1061+
message: "Select AI model provider:",
1062+
choices: [
1063+
{ name: "OpenAI (GPT-4o-mini)", value: OPENAI_PROVIDER },
1064+
{ name: "Google (Gemini 2.5 Flash)", value: GOOGLE_PROVIDER },
1065+
{ name: "Anthropic (Claude 3.5 Sonnet)", value: ANTHROPIC_PROVIDER },
1066+
],
1067+
}) as
1068+
| typeof OPENAI_PROVIDER
1069+
| typeof GOOGLE_PROVIDER
1070+
| typeof ANTHROPIC_PROVIDER;
1071+
1072+
const maxConcurrency = await input({
1073+
message: "Enter maximum concurrent requests (leave empty for unlimited):",
1074+
validate: (value) => {
1075+
if (!value.trim()) return true; // Allow empty for unlimited
1076+
const num = parseInt(value);
1077+
if (isNaN(num) || num <= 0) {
1078+
return "Please enter a positive number or leave empty for unlimited";
1079+
}
1080+
return true;
1081+
},
1082+
});
1083+
1084+
labelingConfig = {
1085+
modelProvider,
1086+
maxConcurrency: maxConcurrency.trim()
1087+
? parseInt(maxConcurrency)
1088+
: undefined,
1089+
};
1090+
1091+
console.info("✅ Labeling configuration added");
1092+
}
1093+
10211094
// Build the config object
10221095
const config: z.infer<typeof localConfigSchema> = {
10231096
language: language,
@@ -1039,5 +1112,10 @@ export async function generateConfig(
10391112
config.c = cConfig;
10401113
}
10411114

1115+
// Add labeling config if it exists
1116+
if (labelingConfig) {
1117+
config.labeling = labelingConfig;
1118+
}
1119+
10421120
return config;
10431121
}

src/cli/handlers/manifest/generate.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ function builder(
4545
}
4646
return value;
4747
},
48+
}).option("labelingApiKey", {
49+
type: "string",
50+
description: "The API key to use for the labeling",
4851
});
4952
}
5053

@@ -128,6 +131,7 @@ async function handler(
128131
branch?: string;
129132
commitSha?: string;
130133
commitShaDate?: string;
134+
labelingApiKey?: string;
131135
},
132136
) {
133137
const napiConfig = argv.napiConfig as z.infer<typeof localConfigSchema>;
@@ -217,7 +221,12 @@ async function handler(
217221

218222
console.info(`📊 Processing ${files.size} files...`);
219223

220-
const dependencyManifest = generateDependencyManifest(files, napiConfig);
224+
const dependencyManifest = await generateDependencyManifest(
225+
files,
226+
napiConfig,
227+
globalConfig,
228+
argv.labelingApiKey,
229+
);
221230

222231
// Upload manifest to API instead of writing to disk
223232
const apiService = new ApiService(

src/cli/handlers/set/apiKey.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import type { Arguments } from "yargs-types";
2+
import type { z } from "zod";
3+
import {
4+
type globalConfigSchema,
5+
setConfig,
6+
} from "../../middlewares/globalConfig.ts";
7+
import {
8+
ANTHROPIC_PROVIDER,
9+
GOOGLE_PROVIDER,
10+
type ModelProvider,
11+
OPENAI_PROVIDER,
12+
} from "../../../manifest/dependencyManifest/labeling/model.ts";
13+
import { input, select } from "@inquirer/prompts";
14+
15+
async function handler(
16+
argv: Arguments & {
17+
globalConfig: z.infer<typeof globalConfigSchema>;
18+
},
19+
) {
20+
const globalConfig = argv.globalConfig as z.infer<typeof globalConfigSchema>;
21+
22+
const provider = await select({
23+
message: "Select a provider",
24+
choices: [
25+
{ name: "Google", value: GOOGLE_PROVIDER },
26+
{ name: "OpenAI", value: OPENAI_PROVIDER },
27+
{ name: "Anthropic", value: ANTHROPIC_PROVIDER },
28+
],
29+
}) as ModelProvider;
30+
31+
const apiKey = await input({
32+
message: "Enter the API key",
33+
validate: (value) => {
34+
if (value.length === 0) {
35+
return "API key cannot be empty";
36+
}
37+
return true;
38+
},
39+
});
40+
41+
const labeling = globalConfig.labeling || { apiKeys: {} };
42+
labeling.apiKeys[provider] = apiKey;
43+
setConfig({ ...globalConfig, labeling });
44+
45+
console.info("API key set successfully");
46+
}
47+
48+
export default {
49+
command: "apiKey",
50+
describe: "set an API key for a model provider in your global config",
51+
builder: () => {},
52+
handler,
53+
};

src/cli/handlers/set/index.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import apiKeyHandler from "./apiKey.ts";
2+
import type { Arguments } from "yargs-types";
3+
import type { globalConfigSchema } from "../../middlewares/globalConfig.ts";
4+
import type { z } from "zod";
5+
6+
function builder(
7+
yargs: Arguments & {
8+
globalConfig: z.infer<typeof globalConfigSchema>;
9+
},
10+
) {
11+
return yargs
12+
.command(apiKeyHandler)
13+
.demandCommand(1, "You need to specify a valid command");
14+
}
15+
16+
export default {
17+
command: "set",
18+
describe: "set a value in the global config",
19+
builder,
20+
handler: () => {},
21+
};

src/cli/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
} from "./middlewares/checkVersion.ts";
66
import loginCommand from "./handlers/login/index.ts";
77
import initCommand from "./handlers/init/index.ts";
8+
import setCommand from "./handlers/set/index.ts";
89
import manifestCommand from "./handlers/manifest/index.ts";
910
import extractCommand from "./handlers/extract/index.ts";
1011
import { globalConfigMiddleware } from "./middlewares/globalConfig.ts";
@@ -27,6 +28,7 @@ export function initCli() {
2728
.middleware(globalConfigMiddleware)
2829
.command(loginCommand)
2930
.command(initCommand)
31+
.command(setCommand)
3032
.command(manifestCommand)
3133
.command(extractCommand)
3234
.demandCommand(1, "You need to specify a command")

src/cli/middlewares/globalConfig.ts

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
import type { Arguments } from "yargs-types";
2-
import { join } from "@std/path";
2+
import { dirname, join } from "@std/path";
33
import z from "zod";
44

55
export const globalConfigSchema = z.object({
66
jwt: z.string().optional(),
77
token: z.string().optional(),
88
apiHost: z.string(),
9+
labeling: z.object({
10+
apiKeys: z.object({
11+
google: z.string().optional(),
12+
openai: z.string().optional(),
13+
anthropic: z.string().optional(),
14+
}),
15+
}).optional(),
916
});
1017

1118
export const defaultApiHost = "https://api.nanoapi.io";
@@ -74,28 +81,44 @@ export function globalConfigMiddleware(
7481
const result = globalConfigSchema.safeParse(JSON.parse(content));
7582
if (!result.success) {
7683
// wrong config, generate a new one
77-
config = defaultConfig;
78-
Deno.writeTextFileSync(configPath, JSON.stringify(config, null, 2));
79-
}
80-
if (result.data) {
81-
config = result.data;
84+
setConfig(config);
85+
args.globalConfig = config;
86+
return;
8287
}
88+
89+
// config is valid, use it
90+
config = result.data;
91+
args.globalConfig = config;
92+
return;
8393
} else {
8494
// no config, generate a new one
85-
config = defaultConfig;
86-
Deno.writeTextFileSync(configPath, JSON.stringify(config, null, 2));
95+
setConfig(config);
96+
args.globalConfig = config;
97+
return;
8798
}
8899
} catch (_error) {
89100
// failed to read or create config, generate a new one
90101
config = defaultConfig;
91-
Deno.writeTextFileSync(configPath, JSON.stringify(config, null, 2));
102+
setConfig(config);
103+
args.globalConfig = config;
104+
return;
92105
}
93-
94-
args.globalConfig = config;
95106
}
96107

97108
export function setConfig(
98109
config: z.infer<typeof globalConfigSchema>,
99110
) {
100-
Deno.writeTextFileSync(getConfigPath(), JSON.stringify(config, null, 2));
111+
const configPath = getConfigPath();
112+
const dir = dirname(configPath);
113+
let dirExists = false;
114+
try {
115+
Deno.statSync(dir);
116+
dirExists = true;
117+
} catch {
118+
dirExists = false;
119+
}
120+
if (!dirExists) {
121+
Deno.mkdirSync(dir, { recursive: true });
122+
}
123+
Deno.writeTextFileSync(configPath, JSON.stringify(config, null, 2));
101124
}

0 commit comments

Comments
 (0)