@sage-protocol/cli 0.3.10 → 0.4.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.
@@ -2,6 +2,7 @@ const fs = require('fs');
2
2
  const path = require('path');
3
3
  const axios = require('axios');
4
4
  const { getAddress, id, parseUnits, formatUnits, Contract, MaxUint256 } = require('ethers');
5
+ const { decryptAesGcm, fromB64 } = require('../utils/aes');
5
6
  const sdk = require('@sage-protocol/sdk');
6
7
  const { formatJson } = require('../utils/format');
7
8
  const { getProvider, getWallet } = require('../utils/provider');
@@ -734,6 +735,10 @@ async function myLicensesAction(opts = {}) {
734
735
  console.log(JSON.stringify(results, null, 2));
735
736
  } else if (results.length === 0) {
736
737
  console.log('ℹ️ No active personal licenses found for this holder.');
738
+ console.log('');
739
+ console.log(' If you recently purchased a license, the subgraph may still be syncing.');
740
+ console.log(' To check a specific license directly on-chain, use:');
741
+ console.log(' sage personal premium access <creator> <key>');
737
742
  } else {
738
743
  results.forEach((item) => {
739
744
  console.log(`🧾 ${item.receiptIdHex} — balance ${item.balance}`);
@@ -897,6 +902,32 @@ async function accessAction(creator, key, opts) {
897
902
 
898
903
  let encryptedCid = resource?.encryptedCid || null;
899
904
  let metadata = resource?.metadata || null;
905
+ let manifestObj = null;
906
+
907
+ // If --manifest provided, fetch manifest from IPFS and extract encryptedCid
908
+ if (opts.manifest) {
909
+ const gateway = resolveGateway(opts.gateway);
910
+ try {
911
+ if (!opts.json) {
912
+ console.log(`📥 Fetching manifest from IPFS: ${opts.manifest}`);
913
+ }
914
+ const manifestResp = await axios.get(`${gateway}${opts.manifest}`, { timeout: 15000 });
915
+ const manifest = manifestResp.data;
916
+ manifestObj = manifest;
917
+ if (manifest?.encryptedCid) {
918
+ encryptedCid = manifest.encryptedCid;
919
+ if (!opts.json) {
920
+ console.log(`✅ Found encryptedCid in manifest: ${encryptedCid}`);
921
+ }
922
+ }
923
+ if (manifest?.metadata && !metadata) {
924
+ metadata = manifest.metadata;
925
+ }
926
+ } catch (err) {
927
+ console.warn(`⚠️ Failed to fetch manifest: ${err?.message || err}`);
928
+ }
929
+ }
930
+
900
931
  if (!encryptedCid && opts.encryptedCid) {
901
932
  encryptedCid = String(opts.encryptedCid);
902
933
  }
@@ -912,9 +943,12 @@ async function accessAction(creator, key, opts) {
912
943
  if (encryptedPayload && looksLikeCid(encryptedPayload)) {
913
944
  try {
914
945
  const resp = await axios.get(`${gateway}${encryptedPayload}`, { timeout: 10000 });
946
+ if (resp.status !== 200) {
947
+ throw new Error(`Status ${resp.status}`);
948
+ }
915
949
  encryptedPayload = resp.data;
916
950
  } catch (error) {
917
- console.warn(`⚠️ Failed to fetch encrypted payload from IPFS via ${gateway}:`, error?.message || error);
951
+ throw new Error(`Failed to fetch encrypted payload from IPFS via ${gateway}: ${error.message}`);
918
952
  }
919
953
  }
920
954
 
@@ -963,15 +997,66 @@ async function accessAction(creator, key, opts) {
963
997
  }
964
998
  }
965
999
 
966
- decrypted = await sdk.personal.decryptWithLit({
967
- encryptedCid: payloadObject,
968
- receiptId,
969
- chain: litChain,
970
- receiptAddress,
971
- litClient: client,
972
- sessionSigs: sessionSigs || undefined,
973
- authSig: authSig || undefined,
974
- });
1000
+ // Check for Hybrid Encryption (Sage Personal Manifest)
1001
+ if (payloadObject.type === 'sage-personal-encrypted' && payloadObject.enc === 'aes-256-gcm') {
1002
+ if (!manifestObj || !manifestObj.lit) {
1003
+ throw new Error('Hybrid encryption detected but no Manifest provided (use --manifest <cid>).');
1004
+ }
1005
+ if (!opts.json) console.log('🔐 Decrypting hybrid content (Lit + AES-GCM)...');
1006
+
1007
+ const litParams = manifestObj.lit;
1008
+ const condition = litParams.evmContractConditions?.[0] || {
1009
+ conditionType: 'evmContract',
1010
+ contractAddress: receiptAddress,
1011
+ functionName: 'balanceOf',
1012
+ functionParams: [':userAddress', receiptId.toString()],
1013
+ functionAbi: {
1014
+ inputs: [{ internalType: 'address', name: 'account', type: 'address' }, { internalType: 'uint256', name: 'id', type: 'uint256' }],
1015
+ name: 'balanceOf',
1016
+ outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
1017
+ stateMutability: 'view',
1018
+ type: 'function',
1019
+ },
1020
+ chain: litChain,
1021
+ returnValueTest: { comparator: '>', value: '0' },
1022
+ };
1023
+
1024
+ const request = {
1025
+ unifiedAccessControlConditions: [condition],
1026
+ chain: litChain,
1027
+ ciphertext: litParams.encryptedSymmetricKey,
1028
+ dataToEncryptHash: litParams.dataToEncryptHash,
1029
+ authSig,
1030
+ sessionSigs,
1031
+ };
1032
+
1033
+ const response = await client.decrypt(request);
1034
+ if (!response || !response.decryptedData) {
1035
+ throw new Error('[personal] Lit decryption of symmetric key failed');
1036
+ }
1037
+
1038
+ // response.decryptedData is Uint8Array
1039
+ const symmetricKey = response.decryptedData;
1040
+
1041
+ decrypted = decryptAesGcm(
1042
+ fromB64(payloadObject.ciphertext),
1043
+ Buffer.from(symmetricKey),
1044
+ fromB64(payloadObject.iv),
1045
+ fromB64(payloadObject.tag)
1046
+ );
1047
+
1048
+ } else {
1049
+ // Legacy/Direct Lit Decryption
1050
+ decrypted = await sdk.personal.decryptWithLit({
1051
+ encryptedCid: payloadObject,
1052
+ receiptId,
1053
+ chain: litChain,
1054
+ receiptAddress,
1055
+ litClient: client,
1056
+ sessionSigs: sessionSigs || undefined,
1057
+ authSig: authSig || undefined,
1058
+ });
1059
+ }
975
1060
 
976
1061
  if (opts.out) {
977
1062
  outputPath = path.resolve(opts.out);
@@ -1112,6 +1197,7 @@ function register(program) {
1112
1197
  .option('--out <file>', 'Write decrypted output to file')
1113
1198
  .option('--skip-decrypt', 'Skip decryption and only report license status')
1114
1199
  .option('--encrypted-cid <cid>', 'Override encrypted CID')
1200
+ .option('--manifest <cid>', 'Manifest CID to fetch encrypted content info from IPFS')
1115
1201
  .option('--json', 'Emit machine-readable JSON report', false)
1116
1202
  .action((creator, key, opts) => accessAction(creator, key, opts).catch((err) => { console.error(err.message); process.exit(1); }));
1117
1203
 
@@ -18,10 +18,10 @@ Create or update your `.env` file with the required configuration:
18
18
 
19
19
  ```bash
20
20
  # Subgraph (preferred) + Blockchain
21
- SUBGRAPH_URL=https://api.goldsky.com/api/public/project_cmhxp0fppsbdd01q56xp2gqw9/subgraphs/sxxx-protocol/1.0.1/gn
22
- RPC_URL=https://sepolia.base.org
23
- LIBRARY_REGISTRY_ADDRESS=0x032F2E93549640a319163Ba600fc50bA1AFBE50F
24
- SUBDAO_FACTORY_ADDRESS=0x6bb6A83149F84Df0584aC863e5fE3416AFb214aC
21
+ SUBGRAPH_URL=https://api.goldsky.com/api/public/project_cmhxp0fppsbdd01q56xp2gqw9/subgraphs/sxxx-protocol/1.0.2/gn
22
+ RPC_URL=https://base-sepolia.publicnode.com
23
+ LIBRARY_REGISTRY_ADDRESS=0x419Cd79d58db6A5aE1900dd7F0c1b8d153FfaD06
24
+ SUBDAO_FACTORY_ADDRESS=0xED573E7e4c00fE325f846B961Df3352807eA3807
25
25
 
26
26
  # Optional: Custom IPFS Gateway
27
27
  IPFS_GATEWAY=https://ipfs.io/ipfs
@@ -65,10 +65,10 @@ You should see a JSON response listing the available tools.
65
65
  "command": "node",
66
66
  "args": ["/absolute/path/to/sage-protocol/cli/mcp-server-stdio.js"],
67
67
  "env": {
68
- "SUBGRAPH_URL": "https://api.goldsky.com/api/public/project_cmhxp0fppsbdd01q56xp2gqw9/subgraphs/sxxx-protocol/1.0.1/gn",
69
- "RPC_URL": "https://sepolia.base.org",
70
- "LIBRARY_REGISTRY_ADDRESS": "0x032F2E93549640a319163Ba600fc50bA1AFBE50F",
71
- "SUBDAO_FACTORY_ADDRESS": "0x6bb6A83149F84Df0584aC863e5fE3416AFb214aC"
68
+ "SUBGRAPH_URL": "https://api.goldsky.com/api/public/project_cmhxp0fppsbdd01q56xp2gqw9/subgraphs/sxxx-protocol/1.0.2/gn",
69
+ "RPC_URL": "https://base-sepolia.publicnode.com",
70
+ "LIBRARY_REGISTRY_ADDRESS": "0x419Cd79d58db6A5aE1900dd7F0c1b8d153FfaD06",
71
+ "SUBDAO_FACTORY_ADDRESS": "0xED573E7e4c00fE325f846B961Df3352807eA3807"
72
72
  }
73
73
  }
74
74
  }
@@ -97,36 +97,37 @@ Can you search for on-chain prompts related to "real estate"?
97
97
  or
98
98
 
99
99
  ```
100
- Please list all available SubDAOs in the Sage Protocol.
100
+ Please list all available DAOs in the Sage Protocol.
101
101
  ```
102
102
 
103
103
  ## Available MCP Tools
104
104
 
105
- Your Sage Protocol MCP Server provides these tools:
106
-
107
- ### 1. `search_onchain_prompts`
108
- - **Purpose**: Search prompts from LibraryRegistry and SubDAO registries
109
- - **Parameters**:
110
- - `query` (optional): Search terms
111
- - `subdao` (optional): Filter by SubDAO address
112
- - `tags` (optional): Filter by tags array
113
- - `limit` (optional): Max results (default: 10)
114
- - `includeContent` (optional): Fetch full content (default: false)
115
-
116
- ### 2. `get_prompt_content`
117
- - **Purpose**: Fetch full prompt content from IPFS
118
- - **Parameters**:
119
- - `cid` (required): IPFS CID of the prompt
120
-
121
- ### 3. `list_subdaos`
122
- - **Purpose**: List all SubDAOs in the protocol
123
- - **Parameters**:
124
- - `limit` (optional): Max results (default: 20)
125
-
126
- ### 4. `get_library_manifests`
127
- - **Purpose**: Get executed library manifests from LibraryRegistry
128
- - **Parameters**:
129
- - `limit` (optional): Max results (default: 10)
105
+ Your Sage Protocol MCP Server provides these tools. For the full list, see `docs/development/mcp-config.md`.
106
+
107
+ ### Quick Workflow (recommended)
108
+ - `quick_create_prompt` - Create a new prompt (handles library creation automatically)
109
+ - `quick_iterate_prompt` - Update an existing prompt with automatic backup
110
+ - `quick_test_prompt` / `test_prompt` - Test a prompt with variable substitution
111
+ - `improve_prompt` - Analyze a prompt and suggest improvements
112
+ - `rename_prompt` - Rename a prompt's key/display name
113
+ - `help` - Get help on Sage MCP workflows
114
+
115
+ ### Discovery & Search
116
+ - `search_prompts` - Unified search across local and on-chain sources
117
+ - `search_onchain_prompts` - Search directly from on-chain registries
118
+ - `trending_prompts` - List trending prompts
119
+ - `list_prompts` - List prompts from local libraries
120
+ - `get_prompt` - Retrieve a specific prompt by key
121
+ - `get_prompt_content` - Fetch full content from IPFS by CID
122
+ - `list_libraries` - Page through libraries (local or on-chain)
123
+ - `list_subdaos` - List all available DAOs
124
+ - `get_library_manifests` - Get executed library manifests
125
+
126
+ ### Publishing & Governance
127
+ - `suggest_subdaos_for_library` - Recommend DAOs for publishing
128
+ - `generate_publishing_commands` - Generate CLI commands to publish
129
+ - `publish_manifest_flow` - Validate Push → Proposal payload (no signing)
130
+ - `list_proposals` - List active proposals for a DAO
130
131
 
131
132
  ## Troubleshooting
132
133
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sage-protocol/cli",
3
- "version": "0.3.10",
3
+ "version": "0.4.0",
4
4
  "description": "Sage Protocol CLI for managing AI prompt libraries",
5
5
  "bin": {
6
6
  "sage": "./bin/sage.js"