Skip to content

feat(cli): add alternative authentication for link command#24

Open
git112 wants to merge 2 commits intoInsForge:mainfrom
git112:main
Open

feat(cli): add alternative authentication for link command#24
git112 wants to merge 2 commits intoInsForge:mainfrom
git112:main

Conversation

@git112
Copy link

@git112 git112 commented Mar 15, 2026

Closes #20

Added --api-base-url and --api-key options to the insforge link command to bypass OAuth. This enables direct project linking for OSS/self-hosted instances, remote servers, CI, headless environments, and TUI tools.

Summary by CodeRabbit

  • New Features

    • Added support for direct linking to self-hosted and open-source project instances using API credentials, bypassing standard OAuth authentication.
  • Chores

    • Updated ignore patterns for AI agent-related directories.

@coderabbitai
Copy link

coderabbitai bot commented Mar 15, 2026

Walkthrough

The pull request adds support for direct CLI linking using API Base URL and API Key, enabling authentication in environments where OAuth is impractical (CI, headless systems, remote servers). It includes new CLI options in the link command, credential handling logic for OSS projects, and .gitignore patterns for IDE and tool directories.

Changes

Cohort / File(s) Summary
Configuration & Environment
.gitignore
Added ignore patterns for IDE and development tool directories (.insforge, .agent, .agents, .augment, .claude, .cline, .github/copilot\*, .kilocode, .qoder, .qwen, .roo, .trae, .windsurf).
Authentication & Linking
src/commands/projects/link.ts, src/lib/credentials.ts
Implemented direct authentication flow using --api-base-url and --api-key options. Includes URL validation, synthetic ProjectConfig creation for OSS projects, early exit logic, and OSS-specific credential bypass in requireAuth to skip OAuth when project_id is oss-project.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐰 With API key and base URL in hand,
No OAuth gates in this promised land,
CI servers rejoice, headless dreams take flight,
While IDE folders hide safely from sight! 🛠️✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning All changes are scoped to implementing alternative authentication. Changes to .gitignore for AI tools appear unrelated to the linked issue objective. Remove AI tool directory patterns from .gitignore or clarify their necessity in the PR description, as they appear unrelated to the authentication feature.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding an alternative authentication method to the link command that bypasses OAuth.
Linked Issues check ✅ Passed The PR implements the core requirements from issue #20: adds --api-base-url and --api-key options, enables direct OSS/self-hosted linking, validates inputs, bypasses OAuth, and maintains existing OAuth flow.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

You can customize the high-level summary generated by CodeRabbit.

Configure the reviews.high_level_summary_instructions setting to provide custom instructions for generating the high-level summary.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
src/commands/projects/link.ts (1)

39-40: Consider validating --api-base-url format.

The apiBaseUrl is used directly in oss_host and later in reportCliUsage for HTTP requests. A malformed URL will cause runtime failures. Consider basic validation (e.g., URL constructor check).

Proposed validation
         if (opts.apiBaseUrl && opts.apiKey) {
+          // Validate URL format
+          try {
+            new URL(opts.apiBaseUrl);
+          } catch {
+            throw new CLIError('Invalid --api-base-url: must be a valid URL.');
+          }
+
           // Direct OSS/Self-hosted linking bypasses OAuth
           const projectConfig: ProjectConfig = {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/projects/link.ts` around lines 39 - 40, Validate the
--api-base-url value before assigning it to oss_host and before calling
reportCliUsage: in the code handling opts (where api_key: opts.apiKey and
oss_host: opts.apiBaseUrl are set) add a basic URL validation (e.g., attempt to
construct new URL(opts.apiBaseUrl) and catch errors) and reject/throw or surface
a clear CLI error if invalid; ensure reportCliUsage and any HTTP client only run
when opts.apiBaseUrl passes this check so malformed URLs cannot be used.
.gitignore (1)

4-4: Minor redundancy: .insforge/ and .insforge both present.

Line 4 already ignores .insforge/ (directory), and line 9 adds .insforge (without slash), which matches both files and directories. The second pattern subsumes the first. Consider removing the duplicate on line 4, or keep both if intentional for clarity.

Also applies to: 9-9

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.gitignore at line 4, The .gitignore contains duplicate patterns for the
same path (".insforge/" and ".insforge"); remove the redundancy by deleting one
of them—prefer keeping the simpler ".insforge" pattern and remove ".insforge/"
(or vice versa if you prefer explicit directory notation) so only a single
ignore entry for insforge remains; update the .gitignore accordingly to
eliminate the duplicate entry.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/commands/projects/link.ts`:
- Around line 31-56: The direct linking branch (where opts.apiBaseUrl &&
opts.apiKey creates projectConfig and calls saveProjectConfig,
installCliGlobally, installSkills, and reportCliUsage) needs its own try-catch
so failures report the correct telemetry; wrap the entire direct-link block in a
try/catch, and in the catch call reportCliUsage('cli.link_direct', false,
<error-code-or-default>) and rethrow or surface the error, while preserving the
existing success path that calls reportCliUsage('cli.link_direct', true, 6).
Ensure the catch references the same symbols: saveProjectConfig,
installCliGlobally, installSkills, and reportCliUsage.
- Around line 31-41: The code currently treats only the case where both
opts.apiBaseUrl and opts.apiKey are present; add a guard to detect when exactly
one is provided (i.e., XOR) and throw a CLIError with a clear message like "Both
--api-base-url and --api-key must be provided for direct linking" to avoid
silently falling back to OAuth; update the block around the existing check of
opts.apiBaseUrl and opts.apiKey (the ProjectConfig construction) to first if
(opts.apiBaseUrl && opts.apiKey) { ... } else if (opts.apiBaseUrl ||
opts.apiKey) { throw new CLIError('Both --api-base-url and --api-key must be
provided for direct linking'); } and ensure CLIError is imported where used.

---

Nitpick comments:
In @.gitignore:
- Line 4: The .gitignore contains duplicate patterns for the same path
(".insforge/" and ".insforge"); remove the redundancy by deleting one of
them—prefer keeping the simpler ".insforge" pattern and remove ".insforge/" (or
vice versa if you prefer explicit directory notation) so only a single ignore
entry for insforge remains; update the .gitignore accordingly to eliminate the
duplicate entry.

In `@src/commands/projects/link.ts`:
- Around line 39-40: Validate the --api-base-url value before assigning it to
oss_host and before calling reportCliUsage: in the code handling opts (where
api_key: opts.apiKey and oss_host: opts.apiBaseUrl are set) add a basic URL
validation (e.g., attempt to construct new URL(opts.apiBaseUrl) and catch
errors) and reject/throw or surface a clear CLI error if invalid; ensure
reportCliUsage and any HTTP client only run when opts.apiBaseUrl passes this
check so malformed URLs cannot be used.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f402beac-2f9b-44a3-aefe-6a99b5a2bec6

📥 Commits

Reviewing files that changed from the base of the PR and between 9c3bebc and 572e89b.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (3)
  • .gitignore
  • skills-lock.json
  • src/commands/projects/link.ts

skills-lock.json Outdated
@@ -0,0 +1,15 @@
{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is involved by the execution of insforge link command, right? I don't think we need add it to repo.

await reportCliUsage('cli.link_direct', true, 6);
return;
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing validation when only one of the two flags is provided

try {
if (opts.apiBaseUrl && opts.apiKey) {
// Direct OSS/Self-hosted linking bypasses OAuth
const projectConfig: ProjectConfig = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No URL validation on --api-base-url

.insforge/
.env
.env.*

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should not be involved here.

Copy link
Member

@jwfing jwfing left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After linked a project with --api-key and --api-base-url, you run any of commands:

  • insforge db tables
  • insforge docs
  • insforge storage
  • etc.
    It always requests to OAuth login first.

@jwfing
Copy link
Member

jwfing commented Mar 15, 2026

It's not a simple setting update, I think testing all commands is necessary for new initialized mothod(link with project metadata directly).
Thanks for you effort again!

@tonychang04
Copy link
Contributor

Please tell us how you tested it! and address the ai concerns!

@git112
Copy link
Author

git112 commented Mar 16, 2026

Thanks for the review! @jwfing @tonychang04

I addressed the feedback:

• Added validation to ensure both --api-base-url and --api-key must be provided together
• Added URL validation for --api-base-url
• Updated requireAuth() to bypass OAuth when project_id === "oss-project"
• Removed unintended files from the repository

Testing performed:

  1. Built CLI using npm run build

  2. Linked project using:
    insforge link --api-base-url --api-key

  3. Verified the following commands work without OAuth login:

    • insforge db tables
    • insforge docs
    • insforge storage

Please let me know if additional changes are needed.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/lib/credentials.ts (1)

8-21: Consider adding a clarifying comment for the OSS credential bypass.

The null-safe access via projConfig?.project_id correctly handles the case when getProjectConfig() returns null. The hardcoded credentials approach is reasonable for OSS projects that authenticate via api_key instead of OAuth tokens.

Adding a brief comment explaining why these placeholder values are acceptable (i.e., OSS commands use api_key from project config rather than these tokens) would help future maintainers understand the design intent.

Suggested documentation
   const projConfig = getProjectConfig();
   if (projConfig?.project_id === 'oss-project') {
+    // OSS/self-hosted projects authenticate via api_key from project config,
+    // not OAuth tokens. Return placeholder credentials to satisfy the type contract.
     return {
       access_token: 'oss-token',
       refresh_token: 'oss-refresh',
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/credentials.ts` around lines 8 - 21, Add a short clarifying comment
above the OSS bypass block that explains why getProjectConfig() is null-safe
checked and why hardcoded placeholder tokens (returned when
projConfig?.project_id === 'oss-project') are acceptable: note that OSS projects
authenticate via api_key in project config (not OAuth tokens), so these
access_token/refresh_token/user values are dummies used only for compatibility
with callers; reference projConfig, getProjectConfig, and the 'oss-project'
project_id so maintainers know the intent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/commands/projects/link.ts`:
- Around line 31-71: Move the validation and direct-link flow into a single
try-catch so all errors for the direct-link path are handled and reported only
as 'cli.link_direct' (avoid letting CLIError thrown by validation bubble to the
outer catch). Specifically, include the checks of opts.apiBaseUrl and
opts.apiKey and the new URL() validation inside the same try block that creates
ProjectConfig, calls saveProjectConfig, installCliGlobally, installSkills, and
reportCliUsage('cli.link_direct', ...); in the catch for that block call
reportCliUsage('cli.link_direct', false) and then either handle/exit (so you
don’t re-throw into the outer catch) or re-throw a transformed error that the
outer handler will ignore for telemetry — the key is to ensure only
reportCliUsage('cli.link_direct', false) runs for direct-link errors and remove
the duplicate reporting from the outer 'cli.link' path.

---

Nitpick comments:
In `@src/lib/credentials.ts`:
- Around line 8-21: Add a short clarifying comment above the OSS bypass block
that explains why getProjectConfig() is null-safe checked and why hardcoded
placeholder tokens (returned when projConfig?.project_id === 'oss-project') are
acceptable: note that OSS projects authenticate via api_key in project config
(not OAuth tokens), so these access_token/refresh_token/user values are dummies
used only for compatibility with callers; reference projConfig,
getProjectConfig, and the 'oss-project' project_id so maintainers know the
intent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 91fe6d56-4f1d-47ca-989d-693aacb1dfc6

📥 Commits

Reviewing files that changed from the base of the PR and between 572e89b and e27d940.

📒 Files selected for processing (2)
  • src/commands/projects/link.ts
  • src/lib/credentials.ts

Comment on lines +31 to +71
if (opts.apiBaseUrl || opts.apiKey) {
if (!opts.apiBaseUrl || !opts.apiKey) {
throw new CLIError('Both --api-base-url and --api-key must be provided together for direct linking.');
}

try {
new URL(opts.apiBaseUrl);
} catch {
throw new CLIError('Invalid --api-base-url. Please provide a valid URL.');
}

try {
// Direct OSS/Self-hosted linking bypasses OAuth
const projectConfig: ProjectConfig = {
project_id: 'oss-project',
project_name: 'oss-project',
org_id: 'oss-org',
appkey: 'oss',
region: 'local',
api_key: opts.apiKey,
oss_host: opts.apiBaseUrl.replace(/\/$/, ''), // remove trailing slash if any
};

saveProjectConfig(projectConfig);

if (json) {
outputJson({ success: true, project: { id: projectConfig.project_id, name: projectConfig.project_name, region: projectConfig.region } });
} else {
outputSuccess(`Linked to direct project at ${projectConfig.oss_host}`);
}

// Install CLI globally and agent skills
await installCliGlobally(json);
await installSkills(json);
await reportCliUsage('cli.link_direct', true, 6);
return;
} catch (err) {
await reportCliUsage('cli.link_direct', false);
throw err;
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Error handling causes inconsistent/double telemetry reporting.

Two issues with the current error flow:

  1. Validation errors report wrong context: CLIError thrown at lines 33 and 39 occurs before the inner try-catch, so they're caught by the outer catch (line 151) and reported as 'cli.link' failures instead of 'cli.link_direct'.

  2. Double reporting: When errors occur inside the inner try-catch (lines 42-70), they report 'cli.link_direct' failure, then re-throw to the outer catch which also reports 'cli.link' failure.

Proposed fix: handle all direct-link errors in one block
       try {
         if (opts.apiBaseUrl || opts.apiKey) {
-          if (!opts.apiBaseUrl || !opts.apiKey) {
-            throw new CLIError('Both --api-base-url and --api-key must be provided together for direct linking.');
-          }
-
-          try {
-            new URL(opts.apiBaseUrl);
-          } catch {
-            throw new CLIError('Invalid --api-base-url. Please provide a valid URL.');
-          }
-
           try {
+            if (!opts.apiBaseUrl || !opts.apiKey) {
+              throw new CLIError('Both --api-base-url and --api-key must be provided together for direct linking.');
+            }
+
+            try {
+              new URL(opts.apiBaseUrl);
+            } catch {
+              throw new CLIError('Invalid --api-base-url. Please provide a valid URL.');
+            }
+
             // Direct OSS/Self-hosted linking bypasses OAuth
             const projectConfig: ProjectConfig = {
               // ... existing code ...
             };
             // ... rest of direct linking ...
             return;
           } catch (err) {
             await reportCliUsage('cli.link_direct', false);
-            throw err;
+            handleError(err, json);
+            return;
           }
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/projects/link.ts` around lines 31 - 71, Move the validation and
direct-link flow into a single try-catch so all errors for the direct-link path
are handled and reported only as 'cli.link_direct' (avoid letting CLIError
thrown by validation bubble to the outer catch). Specifically, include the
checks of opts.apiBaseUrl and opts.apiKey and the new URL() validation inside
the same try block that creates ProjectConfig, calls saveProjectConfig,
installCliGlobally, installSkills, and reportCliUsage('cli.link_direct', ...);
in the catch for that block call reportCliUsage('cli.link_direct', false) and
then either handle/exit (so you don’t re-throw into the outer catch) or re-throw
a transformed error that the outer handler will ignore for telemetry — the key
is to ensure only reportCliUsage('cli.link_direct', false) runs for direct-link
errors and remove the duplicate reporting from the outer 'cli.link' path.

@jwfing
Copy link
Member

jwfing commented Mar 17, 2026

@git112 I think you changed the default behavior for create command:

$ insforge create
┌  Create a new InsForge project
Error: Not authenticated. Run `insforge login` first.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[1 point]Allow cli to link based on api base url and api key

3 participants