@phi-code-admin/phi-code 0.59.4 → 0.59.6

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.
@@ -714,4 +714,104 @@ _Edit this file to customize Phi Code's behavior for your project._
714
714
  }
715
715
  },
716
716
  });
717
+
718
+ // ─── API Key Management Command ─────────────────────────────────
719
+
720
+ pi.registerCommand("api-key", {
721
+ description: "Set or view API keys (usage: /api-key set <provider> <key> | /api-key list)",
722
+ handler: async (args, ctx) => {
723
+ const parts = args.trim().split(/\s+/);
724
+ const action = parts[0]?.toLowerCase();
725
+
726
+ const PROVIDERS: Record<string, { envVar: string; name: string }> = {
727
+ alibaba: { envVar: "ALIBABA_CODING_PLAN_KEY", name: "Alibaba Coding Plan" },
728
+ dashscope: { envVar: "DASHSCOPE_API_KEY", name: "DashScope (Alibaba)" },
729
+ openai: { envVar: "OPENAI_API_KEY", name: "OpenAI" },
730
+ anthropic: { envVar: "ANTHROPIC_API_KEY", name: "Anthropic" },
731
+ google: { envVar: "GOOGLE_API_KEY", name: "Google" },
732
+ openrouter: { envVar: "OPENROUTER_API_KEY", name: "OpenRouter" },
733
+ groq: { envVar: "GROQ_API_KEY", name: "Groq" },
734
+ brave: { envVar: "BRAVE_API_KEY", name: "Brave Search" },
735
+ };
736
+
737
+ if (!action || action === "help") {
738
+ ctx.ui.notify(`**🔑 API Key Management**
739
+
740
+ Usage:
741
+ /api-key set <provider> <key> — Set an API key for this session
742
+ /api-key list — Show configured providers
743
+ /api-key providers — List all supported providers
744
+
745
+ Supported providers: ${Object.keys(PROVIDERS).join(", ")}
746
+
747
+ Example:
748
+ /api-key set alibaba sk-sp-xxx
749
+ /api-key set openai sk-xxx
750
+
751
+ Keys set with /api-key are active for the current session.
752
+ For persistence, set environment variables:
753
+ • Windows: setx ALIBABA_CODING_PLAN_KEY "your-key"
754
+ • Linux/Mac: export ALIBABA_CODING_PLAN_KEY="your-key"`, "info");
755
+ return;
756
+ }
757
+
758
+ if (action === "list") {
759
+ let found = 0;
760
+ let msg = "**🔑 Configured API Keys:**\n";
761
+ for (const [id, info] of Object.entries(PROVIDERS)) {
762
+ const key = process.env[info.envVar];
763
+ if (key) {
764
+ const masked = key.substring(0, 6) + "..." + key.substring(key.length - 4);
765
+ msg += ` ✅ ${info.name} (${id}): ${masked}\n`;
766
+ found++;
767
+ }
768
+ }
769
+ if (found === 0) {
770
+ msg += " ❌ No API keys configured\n";
771
+ msg += "\nUse `/api-key set <provider> <key>` to add one.";
772
+ }
773
+ ctx.ui.notify(msg, "info");
774
+ return;
775
+ }
776
+
777
+ if (action === "providers") {
778
+ let msg = "**Supported Providers:**\n";
779
+ for (const [id, info] of Object.entries(PROVIDERS)) {
780
+ const status = process.env[info.envVar] ? "✅" : "⬜";
781
+ msg += ` ${status} **${id}** — ${info.name} (${info.envVar})\n`;
782
+ }
783
+ ctx.ui.notify(msg, "info");
784
+ return;
785
+ }
786
+
787
+ if (action === "set") {
788
+ const provider = parts[1]?.toLowerCase();
789
+ const key = parts[2];
790
+
791
+ if (!provider || !key) {
792
+ ctx.ui.notify("Usage: /api-key set <provider> <key>\nExample: /api-key set alibaba sk-sp-xxx", "warning");
793
+ return;
794
+ }
795
+
796
+ const info = PROVIDERS[provider];
797
+ if (!info) {
798
+ ctx.ui.notify(`Unknown provider "${provider}". Supported: ${Object.keys(PROVIDERS).join(", ")}`, "error");
799
+ return;
800
+ }
801
+
802
+ // Set in current process environment
803
+ process.env[info.envVar] = key;
804
+ // Also set common aliases
805
+ if (provider === "alibaba") {
806
+ process.env.DASHSCOPE_API_KEY = key;
807
+ }
808
+
809
+ const masked = key.substring(0, 6) + "..." + key.substring(key.length - 4);
810
+ ctx.ui.notify(`✅ **${info.name}** API key set: ${masked}\n\n⚠️ Active for this session only. For persistence:\n • Windows: \`setx ${info.envVar} "${key.substring(0, 6)}..."\`\n • Linux/Mac: Add \`export ${info.envVar}="..."\` to ~/.bashrc`, "info");
811
+ return;
812
+ }
813
+
814
+ ctx.ui.notify("Unknown action. Use: /api-key set|list|providers|help", "warning");
815
+ },
816
+ });
717
817
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phi-code-admin/phi-code",
3
- "version": "0.59.4",
3
+ "version": "0.59.6",
4
4
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
5
5
  "type": "module",
6
6
  "piConfig": {
@@ -29,7 +29,8 @@
29
29
  "extensions",
30
30
  "README.md",
31
31
  "LICENSE",
32
- "CHANGELOG.md"
32
+ "CHANGELOG.md",
33
+ "scripts"
33
34
  ],
34
35
  "scripts": {
35
36
  "clean": "shx rm -rf dist",
@@ -39,7 +40,8 @@
39
40
  "copy-assets": "shx mkdir -p dist/modes/interactive/theme && shx cp src/modes/interactive/theme/*.json dist/modes/interactive/theme/ && shx mkdir -p dist/core/export-html/vendor && shx cp src/core/export-html/template.html src/core/export-html/template.css src/core/export-html/template.js dist/core/export-html/ && shx cp src/core/export-html/vendor/*.js dist/core/export-html/vendor/",
40
41
  "copy-binary-assets": "shx cp package.json dist/ && shx cp README.md dist/ && shx cp CHANGELOG.md dist/ && shx mkdir -p dist/theme && shx cp src/modes/interactive/theme/*.json dist/theme/ && shx mkdir -p dist/export-html/vendor && shx cp src/core/export-html/template.html dist/export-html/ && shx cp src/core/export-html/vendor/*.js dist/export-html/vendor/ && shx cp -r docs dist/ && shx cp -r examples dist/ && shx cp ../../node_modules/@silvia-odwyer/photon-node/photon_rs_bg.wasm dist/",
41
42
  "test": "vitest --run",
42
- "prepublishOnly": "npm run clean && npm run build"
43
+ "prepublishOnly": "npm run clean && npm run build",
44
+ "postinstall": "node scripts/postinstall.cjs"
43
45
  },
44
46
  "dependencies": {
45
47
  "@mariozechner/jiti": "^2.6.2",
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # Migrate sessions from ~/.pi/agent/*.jsonl to proper session directories.
4
+ # This fixes sessions created by the bug in v0.30.0 where sessions were
5
+ # saved to ~/.pi/agent/ instead of ~/.pi/agent/sessions/<encoded-cwd>/.
6
+ #
7
+ # Usage: ./migrate-sessions.sh [--dry-run]
8
+ #
9
+
10
+ set -e
11
+
12
+ AGENT_DIR="${PI_AGENT_DIR:-$HOME/.pi/agent}"
13
+ DRY_RUN=false
14
+
15
+ if [[ "$1" == "--dry-run" ]]; then
16
+ DRY_RUN=true
17
+ echo "Dry run mode - no files will be moved"
18
+ echo
19
+ fi
20
+
21
+ # Find all .jsonl files directly in agent dir (not in subdirectories)
22
+ shopt -s nullglob
23
+ files=("$AGENT_DIR"/*.jsonl)
24
+ shopt -u nullglob
25
+
26
+ if [[ ${#files[@]} -eq 0 ]]; then
27
+ echo "No session files found in $AGENT_DIR"
28
+ exit 0
29
+ fi
30
+
31
+ echo "Found ${#files[@]} session file(s) to migrate"
32
+ echo
33
+
34
+ migrated=0
35
+ failed=0
36
+
37
+ for file in "${files[@]}"; do
38
+ filename=$(basename "$file")
39
+
40
+ # Read first line and extract cwd using jq
41
+ if ! first_line=$(head -1 "$file" 2>/dev/null); then
42
+ echo "SKIP: $filename - cannot read file"
43
+ ((failed++))
44
+ continue
45
+ fi
46
+
47
+ # Parse JSON and extract cwd
48
+ if ! cwd=$(echo "$first_line" | jq -r '.cwd // empty' 2>/dev/null); then
49
+ echo "SKIP: $filename - invalid JSON"
50
+ ((failed++))
51
+ continue
52
+ fi
53
+
54
+ if [[ -z "$cwd" ]]; then
55
+ echo "SKIP: $filename - no cwd in session header"
56
+ ((failed++))
57
+ continue
58
+ fi
59
+
60
+ # Encode cwd: remove leading slash, replace slashes with dashes, wrap with --
61
+ encoded=$(echo "$cwd" | sed 's|^/||' | sed 's|[/:\\]|-|g')
62
+ encoded="--${encoded}--"
63
+
64
+ target_dir="$AGENT_DIR/sessions/$encoded"
65
+ target_file="$target_dir/$filename"
66
+
67
+ if [[ -e "$target_file" ]]; then
68
+ echo "SKIP: $filename - target already exists"
69
+ ((failed++))
70
+ continue
71
+ fi
72
+
73
+ echo "MIGRATE: $filename"
74
+ echo " cwd: $cwd"
75
+ echo " to: $target_dir/"
76
+
77
+ if [[ "$DRY_RUN" == false ]]; then
78
+ mkdir -p "$target_dir"
79
+ mv "$file" "$target_file"
80
+ fi
81
+
82
+ ((migrated++))
83
+ echo
84
+ done
85
+
86
+ echo "---"
87
+ echo "Migrated: $migrated"
88
+ echo "Skipped: $failed"
89
+
90
+ if [[ "$DRY_RUN" == true && $migrated -gt 0 ]]; then
91
+ echo
92
+ echo "Run without --dry-run to perform the migration"
93
+ fi
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Post-install script: copies bundled extensions, agents, and skills
4
+ * to ~/.phi/agent/ so Pi discovers them automatically.
5
+ */
6
+ const { existsSync, mkdirSync, cpSync, readdirSync } = require("fs");
7
+ const { join } = require("path");
8
+ const { homedir } = require("os");
9
+
10
+ const agentDir = join(homedir(), ".phi", "agent");
11
+ const packageDir = __dirname.replace(/[/\\]scripts$/, "");
12
+
13
+ const copies = [
14
+ { src: "extensions/phi", dest: join(agentDir, "extensions"), label: "extensions" },
15
+ { src: "agents", dest: join(agentDir, "agents"), label: "agents" },
16
+ { src: "skills", dest: join(agentDir, "skills"), label: "skills" },
17
+ ];
18
+
19
+ for (const { src, dest, label } of copies) {
20
+ const srcDir = join(packageDir, src);
21
+ if (!existsSync(srcDir)) continue;
22
+
23
+ mkdirSync(dest, { recursive: true });
24
+
25
+ const files = readdirSync(srcDir);
26
+ let copied = 0;
27
+ for (const file of files) {
28
+ const srcPath = join(srcDir, file);
29
+ const destPath = join(dest, file);
30
+ try {
31
+ cpSync(srcPath, destPath, { recursive: true, force: true });
32
+ copied++;
33
+ } catch (e) {
34
+ // Skip files that can't be copied (permissions, etc.)
35
+ }
36
+ }
37
+ if (copied > 0) {
38
+ console.log(` Φ Installed ${copied} ${label} to ${dest}`);
39
+ }
40
+ }